Personal Workbench · 个人工作台

Transformer le fatras quotidien en actifs structurés, consultables et exploitables

Un système d'exploitation personnel qui tourne sur Claude Code. Le stockage est du markdown brut sous git — sans base de données, sans service, sans compte. Un lien, une présentation ou une tâche jetés depuis un téléphone dans un canal Discord sont extraits, classés, mis en gabarit et archivés, pour arriver dans le dépôt sous forme de pull request en attente de relecture.

Stockage markdown + git Exécution Claude Code Entrée pont de sondage REST Discord Règles / contrôles 7 / 6 Début 2026-07-27
01

Ce que cela résout

PROBLEM
Point de frictionCe qui est fait ici
Des tâches éparpillées entre messageries, courriels et mémoire Toutes les actions convergent vers un registre unique, tasks/TASKS.md, dont un script valide le format et dont les identifiants doivent être uniques
Les documents arrivent dans tous les formats — pages web, Word, diapositives, PDF, enregistrements scripts/intake/extract.py les réduit tous à du texte brut, que wb-intake digère ensuite
Archiver n'est pas retrouver — trois mois plus tard, c'est introuvable Gabarits obligatoires et frontmatter YAML, validés par machine, classés selon PARA, avec un index généré
On voit quelque chose d'utile sur son téléphone, et on l'a oublié une fois revenu au bureau On le dépose dans le canal Discord #inbox ; le pont le récupère, Claude le digère et ouvre une PR
02

Le pipeline : d'un dépôt à la volée à un diff relisible

PIPELINE

Trois canaux d'entrée convergent vers un même pipeline. Il n'y a qu'une étape au milieu — la compétence wb-intake extrait, classe et applique un gabarit — et la sortie se répartit vers quatre destinations. Chaque digestion se termine par une pull request plutôt que par une écriture directe sur le tronc : le verrou humain est la seule partie de ce système qui ne peut pas être sautée.

ENTRÉE DIGESTION SORTIE Discord #inbox déposé du téléphone · sondé en REST inbox/pending/ local glisser un fichier dans le dossier le dire dans la session alimenter depuis Claude Code wb-intake extraire → classer → gabarit extract.py + frontmatter imposé daily/2026-07-28.md journal quotidien resources/<area>/xxx.md fiche de ressource · classée par thème projects/xxx/… document de projet tasks/TASKS.md action · un registre unique Pull request · verrou humain Règle 6 : Claude ne fusionne pas sa propre PR 6 scripts de contrôle veillent en CI : frontmatter · registre · classement · secrets · inbox · couverture
FIG. 1 — Trois canaux d'entrée → une étape de digestion → quatre types de sorties → une PR. Toute écriture passe par un diff ; aucun chemin ne contourne la relecture humaine.
03

Pourquoi chaque règle doit porter un script de contrôle

GOVERNANCE

Dans ce système, celui qui écrit tous les jours est Claude, pas moi. Une règle inscrite dans CONTRIBUTING.md sous forme d'un paragraphe de conseils en langue naturelle sera immanquablement diluée après quelques dizaines de sessions. D'où une méta-règle : chaque règle doit porter un contrôle qui s'exécute automatiquement (règle 2), et un contrôle vérifie cette propriété elle-même — all_rules_have_checks.py parcourt la liste des règles, et toute règle sans contrôle correspondant fait passer la CI au rouge.

  • Règle 1 Les documents archivés doivent porter un frontmatter valide — frontmatter_valid.py
  • Règle 2 Chaque règle doit porter un contrôle — all_rules_have_checks.py
  • Règle 3 Le registre des tâches doit être bien formé, à identifiants uniques — tasks_ledger_valid.py
  • Règle 4 La matière brute de l'inbox n'entre jamais dans git — inbox_not_committed.py
  • Règle 5 Aucun secret dans le dépôt — no_secrets.py
  • Règle 6 Claude ne fusionne pas sa propre PR — le verrou humain, garanti par le flux de PR lui-même
  • Règle 7 Les documents sont classés par thème et l'index reste à jour — docs_are_filed.py

Une seule commande, python3 scripts/run_checks.py, les exécute tous. Au vert, le squelette est intact.

04

Trois décisions qui ont fixé la forme

ADR

Une décision mérite sa place dans design/ à un seul critère : le moi futur demandera-t-il « pourquoi cela a-t-il été tranché ainsi ? », et la réponse est-elle absente du code ?

ADR-0001Utiliser Discord comme point d'entrée, ne pas écrire de bot

Un seul utilisateur, aucun budget d'exploitation, et en cas de panne c'est moi qui répare. Discord plutôt que Telegram ou iMessage parce qu'il faut plusieurs canaux pour trier (#inbox / #tasks / #daily / #refs) et de la place pour ajouter plus tard d'autres robots, chacun avec sa fonction. Les plateformes chinoises conviendraient mieux aux conditions réseau et à l'aperçu des fichiers Office, mais elles imposeraient d'entretenir un pont dès le premier jour — contre le principe de faire fonctionner avant d'ajouter de la complexité.

ADR-0002Markdown + git pour le stockage, ni base de données ni Notion

La vraie contrainte n'est pas fonctionnelle : c'est que celui qui écrit est un agent. Markdown + git fait de chaque écriture un diff — condition préalable au verrou de fusion humain, et raison pour laquelle la recherche plein texte se réduit à grep. Aucune dépendance, aucun service, aucun compte ; dans dix ans, cat le lira encore. Le prix à payer : les requêtes complexes demandent un script, et quelques milliers de fichiers finiront par appeler un index.

ADR-0003Remplacer le plugin officiel par un pont de sondage REST fait maison

Le plugin officiel entièrement configuré, le bot ne répondait toujours pas. La cause : il reçoit par la passerelle Discord — un WebSocket — et le WebSocket ne passe pas sur cette machine : le DNS résolvait vers une adresse d'une plage Facebook, et une fois le DNS corrigé, les couches IP et SNI étaient bloquées elles aussi. Après avoir testé sept variantes une à une, la conclusion était nette : seul le WebSocket était bloqué ; le REST fonctionnait parfaitement. Le pont est donc devenu un script Python sans dépendance qui sonde l'API REST de Discord, urllib lisant nativement https_proxy.

Méthode

Le critère de ce diagnostic n'était pas « la connexion est établie » mais la trame HELLO (op:10) que la passerelle Discord doit envoyer après la poignée de main. Ne guetter que « c'est connecté » produit des faux positifs — trois processus restaient en SYN_SENT tout en ayant l'air de tourner. Choisir un critère incapable de mentir est la seule chose qui fasse gagner du temps sur ce type de problème réseau.

05

Le faire vivre tout seul

RUNTIME
  • launchd comme service permanent — le pont de sondage est enregistré auprès de launchd sur macOS : il démarre à l'ouverture de session et redémarre après un plantage. Le piège : ProgramArguments doit nommer le chemin réel de l'interpréteur, pas le raccourci /usr/bin/python3, sinon le contrôle de permissions s'attache au mauvais binaire.
  • Un verrou de processus — au début, deux instances de sondage tournaient de front et écrasaient mutuellement leur point de reprise, d'où des messages en double ou perdus. Un verrou de fichier garantit désormais un seul consommateur à la fois.
  • Les échecs de permission ne sont plus silencieux — un refus se contentait auparavant de ne rien faire ; il lève maintenant une erreur explicite, car « on dirait que ça tourne, mais non » est le mode de défaillance le plus coûteux.
  • Pièces jointes vérifiées pour de bon — le chemin des pièces jointes Discord n'a compté qu'une fois qu'il eut acheminé de bout en bout un rapport sectoriel et un lot de 26 captures d'écran, et non parce que le code semblait correct.
06

Filiation

LINEAGE

Les mécanismes de gouvernance — chaque règle porte un contrôle, les contrats agent-loop / agent-verify, garder CLAUDE.md court — sont distillés d'un projet antérieur et d'une boîte à outils construite en parallèle ; la forme du pipeline « inbox → PR » est empruntée à l'éclaireur de références de ce projet. Autrement dit, ce n'est pas un flux conçu à partir de rien, mais deux ensembles de pratiques déjà éprouvées en conditions réelles, ramenés au plus petit squelette qu'une seule personne puisse entretenir.

Dépôt personal-workbench (privé) Compétences wb-intake / wb-daily / wb-task / wb-brief / wb-loop / wb-verify Contrôles python3 scripts/run_checks.py → OK: 6 check(s) passed Rév. 2026-07-28