Il y a moins d’un mois j’ai écrit un post entier pour expliquer comment utiliser trois couches de mémoire avec Claude Code : Linear pour la stratégie, Beads pour la tactique et Tasks pour l’exécution. Une jolie pyramide, bien élégante.

Ben non.

Aujourd’hui je mets Beads à la retraite. Pas par caprice, mais parce que la réalité s’est chargée de démontrer qu’un outil qui te cause plus de problèmes qu’il n’en résout, c’est pas un outil. C’est un boulet.

Ce que Beads apportait

Pour ceux qui n’ont pas lu le post original, Beads était un issue tracker basé sur git. Un plugin pour Claude Code qui stockait les issues dans des fichiers JSONL à l’intérieur de ton repo. L’idée était brillante sur le papier :

  • Persistance dans git : les issues vivaient dans .beads/ et se commitaient avec ton code.
  • Dépendances : un issue pouvait en bloquer un autre.
  • Offline : fonctionnait sans connexion.
  • Le LLM le voyait directement : pas d’APIs, pas de configuration. L’agent lisait les fichiers et voilà.

La promesse : une couche intermédiaire entre “ce que je veux faire cette semaine” (Linear) et “ce que je fais maintenant” (Tasks). La colle tactique.

Ce qui a foiré

Tout allait bien jusqu’à ce que ça n’aille plus. Et ça a cessé de fonctionner de façons créatives.

Le daemon de l’enfer

Beads fait tourner un daemon en arrière-plan pour gérer la base de données SQLite et synchroniser avec git. Ça paraît raisonnable. En pratique :

DATABASE MISMATCH DETECTED!

This database belongs to a different repository:
  Database repo ID:  d1f9ca0c
  Current repo ID:   01eac8ea

⚠️ CRITICAL: This mismatch can cause beads to incorrectly
   delete issues during sync!

Ce message te saute à la figure au démarrage de chaque session. Le daemon plante, la synchro plante, et tu te retrouves avec des issues qui existent dans ton SQLite local mais pas dans git, ou l’inverse. Un état quantique de bugs : tes issues existent et n’existent pas en même temps.

La synchronisation fantôme

bd sync c’est la commande pour synchroniser tes issues avec le remote git. Sauf quand ça marche pas :

→ Pulling from remote...
Error: pulling: git pull failed: exit status 1
remote: Repository not found
fatal: repository 'https://git.frr.dev/frr/wuwei.git/' not found

En fait, beads récupère le remote depuis git, mais si t’as plusieurs remotes (truc courant), il peut choisir le mauvais. Et si ce remote pointe vers un repo qui n’existe pas ou qui a changé de nom, le daemon te crache des erreurs à chaque opération. En silence, tes issues arrêtent de se synchroniser et tu le réalises que quand t’ouvres une autre session et que tout a disparu.

Le coût cognitif

Chaque session avec Claude Code commençait comme ça :

  1. Claude lit le prompt de beads (injecté via hooks)
  2. Le daemon essaie de démarrer
  3. Plante avec une erreur de mismatch
  4. Claude essaie bd sync
  5. Plante avec une erreur de remote
  6. Tu lui dis “ignore ça”
  7. Maintenant oui, on peut bosser

Six étapes de friction avant de faire quoi que ce soit de productif. Six étapes qui consomment du contexte, du temps et de la patience.

Ce qui a changé

Deux choses ont fait passer Beads de “outil utile avec des bugs” à “overhead inutile” :

1. Tasks a mûri

Quand j’ai écrit le post des trois couches, Tasks était basique. Maintenant il a :

  • TaskCreate avec des descriptions, activeForm pour les spinners, et des métadonnées
  • TaskUpdate avec des dépendances (addBlocks, addBlockedBy)
  • TaskList et TaskGet pour l’inspection
  • Persistance optionnelle entre sessions avec CLAUDE_CODE_TASK_LIST_ID

Dit clairement : Tasks fait maintenant tout ce que Beads faisait pour le travail intra-session. Et il le fait sans daemon, sans SQLite, sans synchro git, et sans erreurs fantômes.

2. Le CLI de Linear est arrivé

Le MCP de Linear était, en étant généreux, de la merde. Intermittent, lent, et avec un talent spécial pour planter pile quand t’en avais le plus besoin.

L’alternative c’était taper directement sur l’API avec GraphQL. Ce qui marche, oui, jusqu’à ce que t’essaies de mettre des caractères spéciaux dans les descriptions des issues :

1
2
3
4
5
6
7
8
# Tentative 1 : bash avec interpolation de strings
# Résultat : JSON cassé par les parenthèses et les flèches

# Tentative 2 : Python avec urllib
# Résultat : 401 parce que op read s'évalue pas dans Python

# Tentative 3 : pleurer un peu
# Résultat : cathartique mais improductif

Et puis j’ai découvert le linear CLI :

1
2
brew install schpet/tap/linear
linear auth login -k "$(op read 'op://FRR DEV/Linear/api-key')"

Et créer un issue devient :

1
2
3
4
5
6
linear issue create --team RST --no-interactive \
  -t "Port: agent/loop" \
  --project "Phase 1: Core Loop" \
  --priority 1 \
  -l port \
  -d "Port agent loop (476 LOC). Heart of the agent."

Pas d’escaping GraphQL. Pas de daemon. Pas de synchro cassée. J’ai créé 49 issues en moins d’une minute avec un script bash. Avec le MCP de Linear et l’API, ça m’aurait pris une heure et demie de bagarre avec l’échappement des caractères.

La nouvelle pyramide (qui n’est plus une pyramide)

Le modèle à trois couches était joli. Mais la réalité c’est que deux couches suffisent :

BesoinAvantMaintenant
Vision stratégiqueLinear (MCP/API)Linear (linear CLI)
Travail tactiqueBeadsLinear (linear CLI)
Exécution en sessionTasksTasks

Linear + son CLI couvre la stratégie et la tactique. Tasks couvre l’exécution. Beads ne couvre rien que les deux autres ne fassent mieux.

Avant :
Linear (semaines) → Beads (jours) → Tasks (heures)

Maintenant :
Linear (semaines/jours) → Tasks (heures)

Moins de couches, moins de friction, moins de trucs qui peuvent casser.

Leçon apprise

Ça me rappelle un truc que je dis tout le temps : la complexité inutile, c’est l’ennemi silencieux. Beads résolvait un vrai problème (l’amnésie du LLM entre les sessions), mais il le faisait en ajoutant une couche d’infrastructure qui, sur la durée, générait plus de problèmes qu’elle n’en résolvait.

C’est toujours la même histoire en ingénierie : la bonne solution parfois c’est pas d’ajouter quelque chose, mais de réaliser que ce que t’as déjà s’est suffisamment amélioré pour que t’en aies plus besoin.

Le MCP de Linear c’était de la merde –> le CLI est arrivé. Tasks était basique –> il a mûri. Beads s’est retrouvé au milieu, sans place.

Le rm -rf final

1
2
rm -rf .beads/
git add -A && git commit -m "chore: remove beads"

Deux lignes. C’est comme ça qu’on met un outil à la retraite. Pas de cérémonie, pas de drame.

Beads a été utile tant qu’il l’a été. Maintenant il ne l’est plus. Et c’est très bien comme ça.


TL;DR : Je retire Beads de mon flux de travail avec Claude Code. Le CLI de Linear (schpet/linear-cli) gère les issues sans les galères du MCP ni de l’API GraphQL. Tasks a suffisamment mûri pour couvrir le tracking intra-session. Deux couches suffisent : Linear pour la stratégie et la tactique, Tasks pour l’exécution.

Posts précédents sur le sujet :