Quand j'ai publié KittyClaw il y a deux semaines, l'outil faisait une seule chose : servir de board. Les agents Claude, je les lançais à côté — d'abord à la main, puis via un dispatcher.mjs : un script Node qui polait l'API de KittyClaw, et qui déclenchait les bons agents en fonction de qui était assigné à quel ticket.

Ce dispatcher a très bien marché. Il a orchestré les 13 agents d'Aekan pendant des semaines. Mais c'était un processus externe : un node dispatcher.mjs de plus à lancer, un fichier de state (dispatch-state.json) à garder synchronisé, des logs à aller chercher dans .agents/channel/debug.log, une config à dupliquer d'un projet à l'autre en copiant-collant du JS.

Aujourd'hui, le dispatcher n'existe plus. L'orchestration est dans KittyClaw. Je lance dotnet run sur KittyClaw, rien d'autre. Les 13 agents d'Aekan tournent comme avant — mais l'infra qui les déclenche est maintenant un first-class citizen du board.

Ce basculement du « dispatcher à côté » au « dispatcher dans le board » est petit en lignes de code, mais il change complètement ce qu'est l'outil. Et ce qu'il change dans ma façon de bosser.


Avant : deux process à lancer, deux endroits où regarder

L'ancien workflow tenait en trois couches empilées :

  1. KittyClaw — le board, avec son UI et son API REST.
  2. dispatcher.mjs — un script Node séparé, dans .agents/channel/ du projet, lancé manuellement en terminal.
  3. Claude Code — les agents eux-mêmes, lancés par le dispatcher.

Ça marchait. Mais chaque projet avait son propre dispatcher.mjs, souvent forké depuis Aekan et adapté à la main. Les patterns se dupliquaient : polling à 30s, lock code, debounce de l'evaluator, budget journalier. Quand je voulais ajouter une feature (genre boardIdle ou subTicketStatus), je devais la re-coder dans chaque dispatcher, ou accepter qu'un projet l'ait et pas les autres.

Et visuellement, l'orchestration était invisible depuis le board. Si je voulais savoir ce que faisait un agent en live, j'ouvrais un terminal, je tail -f le log du dispatcher, je croisais avec le board. Deux endroits, deux vocabulaires.

Le board ne savait pas qu'il y avait un dispatcher. Le dispatcher ne montrait rien dans le board. Ils communiquaient via l'API et un fichier JSON — parfaitement fonctionnel, mais découplé au point de ne pas se connaître.


Le déclic : le dispatcher avait sa place dans KittyClaw

Ce qui m'a poussé à basculer, c'est l'accumulation de frictions petites mais répétées. À chaque nouveau projet, je dupliquais le dispatcher.mjs. À chaque nouvelle feature utile (comme le wake CEO sur idle board, développé pour Lain), je me demandais « est-ce que je le back-porte sur Aekan ? ». À chaque bug, je me demandais « lequel des trois dispatchers en tourne ? ».

Et surtout : KittyClaw avait déjà toutes les infos. Les tickets, les assignments, les columns, les members flagués comme agents, l'historique. Le dispatcher ne faisait que relire ce que le board savait déjà, pour en déduire quoi lancer. C'était une duplication de raisonnement, pas de data.

Si c'est répétitif entre projets, c'est factorisable dans l'engine. Si le board connaît déjà l'état, c'est lui qui est le mieux placé pour réagir.


Après : le board est l'orchestrateur

Le nouveau workflow tient en une étape :

  1. Je crée un ticket sur le board, assigné à @programmer.

C'est tout. Trente secondes plus tard, KittyClaw voit le ticket dans la colonne Todo avec l'assignee programmer, le déplace en InProgress, et lance Claude Code avec le skill programmer.md et le contexte du ticket. Les outputs remontent en stream dans un panneau de run live. Quand l'agent termine, le ticket est prêt pour review.

Ce qui rend ce workflow possible, c'est un fichier automations.json dans le workspace du projet :

{
  "trigger": { "type": "ticketInColumn", "columns": ["Todo", "InProgress"],
               "assigneeSlug": "programmer", "seconds": 30 },
  "actions": [
    { "type": "moveTicketStatus", "to": "InProgress" },
    { "type": "runClaudeSkill", "skillFile": "skills/programmer.md",
      "concurrencyGroup": "code", "maxTurns": 200 }
  ]
}

Ce n'est pas un moteur magique. C'est un poll de 30 s qui lit le board, matche un pattern, et lance un process claude avec le bon skill et le bon contexte. Mais dès que c'est en place, je ne suis plus dans la boucle. Je deviens superviseur.


Ce qui change mentalement

Le gain de temps est réel — plus de switching, plus de sessions à retrouver, plus de commandes à retaper. Mais le vrai changement est ailleurs.

Avec le dispatcher externe, j'avais déjà sorti mes agents du mode « conversation one-on-one ». Le board + le dispatcher formaient une équipe qui tournait. Mais l'ensemble était fragmenté : je regardais le board pour décider, puis un terminal pour voir ce que faisaient les agents, puis les logs pour déboguer. Trois vues pour un seul système.

Maintenant, le board est le système. Je vois l'état global, j'assigne, je laisse l'infra dispatcher, et je regarde les runs en stream depuis le ticket lui-même. Sur Aekan, 13 agents tournent comme ça, y compris un documentalist qui se réveille à chaque commit, un code-janitor qui passe tous les jours à 3h du matin, et un evaluator qui vérifie la qualité quand un ticket passe à Done.

Je ne gère plus des conversations. Je gère une file d'attente.

Ce changement de métaphore a l'air anodin, mais il est profond. Tu ne peux pas avoir 13 conversations simultanées dans ta tête. Tu peux très bien superviser une file d'attente de 50 tâches si l'outil te montre ce qui compte.


Ce qui reste à moi

Je lis trop souvent des threads « l'IA va tout faire à ma place », et je tenais à être clair : ce qui compte, c'est toujours moi qui le fais.

  • Décider ce qui vaut d'être fait.
  • Écrire un ticket assez clair pour que l'agent comprenne.
  • Reviewer ce qui sort, accepter ou renvoyer avec du feedback.
  • Identifier les patterns qui méritent d'être automatisés.
  • Sculpter l'outil quand il frotte.

Ce que l'intégration m'enlève, c'est exactement ce qui n'avait aucune valeur ajoutée : maintenir un dispatcher par projet, corréler trois vues, porter manuellement une feature d'un projet à l'autre. Ce qu'elle me laisse, c'est tout le reste — qui est précisément le travail intéressant.


Pourquoi c'est dans le board et pas ailleurs

On pourrait imaginer un outil séparé : un orchestrateur dédié, qui lit des tickets Jira/Linear et lance des agents. J'ai eu ce moment d'hésitation. Mais le board sait déjà qui est agent, qui est humain, quel est l'état des tickets, qui est bloqué, qui est prêt pour review. Extraire cette information pour la donner à un autre outil, c'est dupliquer une source de vérité — et créer deux endroits à garder synchronisés.

Mettre l'orchestration dans le board, c'est reconnaître que le board est déjà le plan d'exécution. Il ne manquait que l'exécuteur.

Et du côté de l'expérience : quand un agent tourne, les events de stream (outils appelés, fichiers modifiés, tokens consommés) arrivent dans un panneau qui s'ouvre depuis le ticket. Pas d'onglet séparé, pas de dashboard séparé. Le board est le dashboard.


Ce que l'intégration débloque

Certaines automations existaient déjà dans le dispatcher.mjsdocumentalist sur commit, evaluator sur passage à Done, code-janitor quotidien. Ce que l'intégration dans KittyClaw change, ce n'est pas leur existence, c'est leur portée :

  • Une feature = tous les projets. Quand j'ai ajouté boardIdle pour le CEO wake de Lain, Aekan et HolybotsRisingApps l'ont eu gratuitement. Avec trois dispatchers séparés, il aurait fallu back-porter à la main.
  • Budget journalier centralisé. Le cost-log.jsonl est lu par l'engine unique, le seuil (dailyBudgetUsd: 70) bloque tout non-CEO dès qu'il est franchi. Mettre ça dans trois dispatchers indépendants aurait demandé trois implémentations à garder en sync.
  • Reload à chaud. POST /api/projects/{slug}/automations/reload — je change un champ dans automations.json, et c'est en prod. Plus de redémarrage de process Node.
  • Observabilité unifiée. Chaque run est un objet dans l'engine, consultable via l'API, streamé dans le panneau du ticket. Plus de tail -f sur trois fichiers log.

La bonne façon de le dire : les mêmes comportements, mais comme infra au lieu de scripts.


La suite

Le prochain article plonge dans l'anatomie technique : qu'est-ce qui se passe exactement entre le moment où je crée un ticket et le moment où Claude commence à bosser. Quel contexte est injecté, comment les sessions sont préservées entre runs, comment les groupes de concurrence empêchent deux agents de marcher sur les mêmes fichiers.

Mais la leçon de fond est déjà là : un board n'est pas un outil de suivi. C'est un plan d'exécution qui attendait d'être branché.


Le code

Tout est sur GitHub :

github.com/Ekioo/KittyClaw

Si tu construis des outils sur-mesure autour de tes agents, ou si tu veux discuter de cette approche, rejoins-moi sur Discord.

Rejoindre le Discord