CommunityRédaction et éditiongithub.com

agent-sync — synchronisation d'agents

Plusieurs agents de codage, un seul dépôt, aucune collision — baux avec TTL, réservation d'ID sans conflit, journal d'exécution et tableau généré sur un nuage de connaissances enfichable. Livré dans le bundle sshlg-skills.

Qu'est-ce que agent-sync — synchronisation d'agents ?

`agent-sync` est un outil de coordination conçu pour les environnements multi-agents (tels que Claude Code, Cursor ou Codex) travaillant sur un seul dépôt Git. Il résout le problème fondamental des collisions d'écriture en introduisant un système de baux (leases) avec TTL et un plan de coordination basé sur un nuage de connaissances optionnel (Outline). La règle d'or est que Git reste le plan d'enregistrement des faits durables, tandis que le nuage gère l'état transitoire de "qui fait quoi maintenant". L'outil fournit un ensemble complet de commandes pour l'initialisation (`init`), la réservation d'ID (`reserve`), l'acquisition/libération de baux (`acquire`/`release`), la journalisation des actions, la gestion des fusions (`merge`), la validation de la configuration (`check`) et la génération d'un tableau de bord partagé (`board`). Il détecte et refuse les configurations dangereuses (fichiers .env suivis par Git, registres sans ligne de base, hooks absents). Chaque commande est conçue pour être idempotente et communicative, garantissant que les agents agissent sur des informations à jour et non sur des hypothèses obsolètes. La documentation inclut des références détaillées sur le protocole de bail, le modèle de branchement et la structure des registres de décision.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add ssheleg/agent-sync

Installed? Explore more Rédaction et édition skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

agent-sync — un projet, de nombreux agents, aucune collision

Deux plans, et une règle entre eux :

Git est le plan d'enregistrement. Le nuage est le plan de coordination. Un fait qui doit survivre est écrit d'abord dans Git et référencé depuis le nuage. Un fait sur qui fait quoi maintenant vit dans le nuage et expire.

Aucun objet du nuage n'est jamais le seul foyer d'un fait durable. Tout ce qui suit existe pour maintenir cela vrai pendant que plusieurs agents écrivent simultanément.

Quatre pièges — lisez ceci avant toute autre chose

1. La base de connaissances ne décide jamais d'un bail. C'est impossible : douze ajouts simultanés à un même document Outline ont renvoyé douze succès et laissé trois lignes. L'exclusion vient de quelque chose qui a un véritable compare-and-swap. Le plan porte l'enregistrement et rien d'autre. Mesures complètes dans references/lease-protocol.md.

2. Sachez quel bail vous possédez, et dites-le. leaseBackend: "local" est une création de fichier atomique — exclusive entre processus sur un seul système de fichiers, consultative entre machines. leaseBackend: "git" pousse une référence, et le rejet non-fast-forward du serveur distant est un compare-and-swap — exclusif entre machines. acquire imprime lequel. Un bail simulé est pire que pas de bail : l'autre agent cesse de vérifier.

acquire écrit également la revendication dans la feuille de route, et release restaure exactement ce qui était là — une ligne, une cellule, refusé en cas d'ambiguïté, git diff vide après un aller-retour. Lisez references/roadmap.md avant de configurer claimTags ou de fermer une tâche ; fermer est une déclaration sur le travail et reste vôtre.

Travaillez sur une branche ; la branche d'intégration est la base stable de quelqu'un d'autre. acquire écrit la revendication seulement là ; sur toute autre branche, le détenteur reste dans le plan de coordination, où status le montre à chaque agent sans que personne n'ait à récupérer votre branche. Validée sur une branche, une revendication est invisible jusqu'à la fusion et transforme la feuille de route partagée en un fichier que deux branches modifient toutes deux. Atterrissez le travail avec merge : les conflits calculés par git merge-tree avant que quoi que ce soit ne soit touché, nommés et refusés s'il y en a, la fusion enregistrée dans docs/MERGES.md (jours récents en entier, plus anciens compactés à l'écriture), le bail --key libéré. merges indique au prochain agent ce qui a atterri pendant son absence. Lisez references/branching.md avant de fusionner.

3. Les hooks existent uniquement dans Claude Code. Ailleurs, rien ne bloque une modification gardée : exécutez guard vous-même et enregistrez l'exécution comme ungated. Ne décrivez pas un projet comme protégé quand il ne l'est pas.

4. Analysez libéralement, et n'appelez jamais un journal illisible une course perdue. Le magasin réécrit ce que vous avez écrit — Outline transforme une puce - en * . Émettez - , acceptez -/*/+, comptez tout ce qui a la forme d'une entrée mais échoue, et échouez bruyamment au-delà de 2% d'éléments non analysables. Signaler perdu alors que la vérité est illisible nomme un détenteur qui n'existe pas. Méfiez-vous d'un pré-filtre silencieux : un continue avant l'expression régulière cache les mauvaises lignes du compteur conçu pour les exposer.

Intégrer ceci dans N'IMPORTE QUEL projet — toute la chaîne

scaffold  → créer l'architecture documentaire, uniquement là où elle est absente
adopt     → lire le dépôt, proposer une configuration, n'écrire rien
init      → écrire la configuration approuvée + le fichier d'environnement ignoré par git
            (l'opérateur colle le jeton — jamais vous)
reconcile --set-baseline    → faire de l'historique un backlog compté, une fois
setup     → générer l'instantané qui décrit le câblage de ce projet
check     → valider l'ensemble ; non nul si ce n'est pas sain

check est ce qui rend la compétence autonome. Elle refuse d'appeler une configuration saine sur une règle qui ne protège rien (un registre, un glob de garde, un modèle de revendication, une porte ou une source miroir pointant vers ce qui n'existe pas), sur des informations d'identification manquantes, sur un fichier d'environnement suivi par git — la seule erreur irrécupérable ici — sur un instantané obsolète ou délié, et sur un registre sans ligne de base. Elle nomme chacun ; chacun a échoué pour de vrai lors de la propre adoption de cet outil.

Exécutez check après avoir adopté, après avoir modifié la configuration, et dans le CI.

scaffold n'écrase jamais. Il initialise un registre de décisions avec une ligne d'allocation et un AGENTS.md qui pointe vers l'instantané, et laisse chaque fichier existant intact — un outil qui réécrit les propres conventions d'un projet lors de l'adoption est pire qu'un outil qui ne fait rien.

Projet existant : commencez par adopt

Exécutez adopt avant init. Il lit le dépôt et imprime ce qu'il a trouvé — registres d'ID, fichiers de registre, portes — plus les décisions qu'il refuse de prendre pour vous — puis propose une configuration.

python3 "$SKILL_DIR/scripts/agent_sync.py" adopt

Confirmez d'abord les registres et les fichiers gardés avec l'opérateur : un registre pointant vers le mauvais fichier rend chaque vérification ultérieure faussement confiante, et une liste gardée qui manque un fichier partagé laisse le seul endroit où les collisions se produisent sans protection. Dans un sous-module, il ne déclare aucun registre : les décisions appartiennent au dépôt parent. Suivez ensuite la chaîne ci-dessus à partir de init.

Première commande dans un projet : init

N'exécutez jamais rien d'autre sur un projet non initialisé. init est l'endroit où la question de stockage est posée et répondue, une fois, et écrite.

Demandez ces deux choses à l'opérateur dans le chat — ne devinez pas, ne choisissez pas de valeur par défaut :

  1. Où l'état de coordination doit-il résider ?

    • un nuage de connaissances (outline) — l'enregistrement partagé, la conscience et le tableau entre machines. Cela ne décide pas des baux ; rien dedans ne le peut (piège 1) ;
    • ou des fichiers locaux (fs) — aucune information d'identification, et aucune visibilité pour un agent sur une autre machine : pas de conscience partagée, pas de signaux inter-dépôts, pas de tableau partagé.

    Le bail est décidé séparément par leaseBackend (piège 2), et gated suit ce choix, jamais le plan d'enregistrement. Rapportez la garantie que vous avez réellement, pas la plus forte que le plan d'enregistrement suggère.

  2. Si nuage : l'URL de l'instance. L'URL est une configuration, pas un secret, donc vous pouvez l'écrire. Le jeton ne l'est pas — vous ne le demandez jamais dans le chat, ne le relisez jamais, et ne le placez jamais vous-même.

Exécutez-le ensuite avec leurs réponses :

python3 "$SKILL_DIR/scripts/agent_sync.py" init --backend outline --url https://<leur-instance>
python3 "$SKILL_DIR/scripts/agent_sync.py" init --backend fs

init écrit .claude/agent-sync.json (forme, validée), écrit .env.agent-sync avec les clés et une ligne de jeton vide (identité, mode 600), ajoute .env.agent-sync et .agent-sync/ à .gitignore, puis imprime exactement ce que l'opérateur doit faire lui-même — créer le jeton dans sa propre instance et le coller dans cette seule ligne. Il n'écrase jamais une configuration ou un fichier d'environnement existant sans --force.

Transmettez ces instructions de clôture textuellement à l'opérateur. Obtenir le jeton dans le fichier est son étape, et la conception dépend du fait qu'il reste le sien.

Ensuite, avant chaque session

python3 "$SKILL_DIR/scripts/agent_sync.py" status

Idempotent. Inspecte, répare, rapporte, nomme EXACTEMENT une prochaine action — et porte le verdict de check, de sorte que la commande que chaque session exécute et celle qui valide la configuration ne peuvent pas donner deux réponses concernant un même projet.

Lisez les deux sections de conscience qu'il imprime — elles sont le point, pas la décoration.

  • Autres exécutions travaillant sur ce projet en ce moment. Qui détient quoi, cette minute. Ne les prenez pas en charge, et ne "jetez pas un œil" aux fichiers qu'elles couvrent. Un bail que vous ne pouvez pas voir vous rend bloqué ; un bail que vous pouvez voir vous rend coordonné.
  • Nouveau depuis votre dernier regard. Les mouvements de dépendances inter-dépôts qui ont atterri pendant que vous étiez absent. Une dépendance qui a bougé peut débloquer ce que vous aviez prévu — ou l'invalider. Cette liste est filigranée par exécution, donc elle reste silencieuse jusqu'à ce que quelque chose change réellement ; quand elle parle, c'est important.

Un agent qui saute ce bloc va redériver le travail que quelqu'un d'autre fait et agir sur un état de dépendance qui a changé il y a une heure.

Ce que status décide d'autre : pas d'identifiants → mode dégradé, signalé, et il continue (un mode plus petit, pas une erreur) ; task-pipeline absent → il imprime la ligne d'installation et s'arrête. N'improvisez pas un flux de substitution — sans ces étapes, il n'y a rien à lier.

npx sshlg-skills install

Les commandes

CommandeFait
initExécutez d'abord. Demandez où l'état réside, écrivez la configuration + le fichier d'environnement ignoré par git, imprimez l'étape de l'opérateur
statusInspecter, réparer, rapporter, nommer une prochaine action
bootstrapCréer le conteneur du nuage et imprimer l'ID à coller dans le fichier d'environnement
acquire <CLÉ>Prendre le bail sur un ID de tâche. Imprime gagné ou perdu <détenteur>
renew <CLÉ>Prolonger le bail. Le hook PostToolUse le fait pour vous
release <CLÉ>Rendre le bail. Faites-le toujours, y compris en cas d'échec
reserve <REG>Réserver le prochain ID dans un registre (DEC, OQ, DEP, …). Imprime l'ID
release-id <REG> <ID>Retourner un ID que vous n'avez finalement pas écrit dans git
journal <texte>Ajouter une ligne au journal de cette exécution
record <texte>Ajouter ce que vous avez réellement construit — --decision DEC-…, --files a,b
reconcileIntention (git) vs réalisé (nuage). --set-baseline une fois par projet
signal <DEP-ID> <état>Déplacer une dépendance inter-dépôts : filed/accepted/delivered/closed/refused
guard <chemin>Répondre si cette exécution peut écrire ce chemin. Code de sortie 0 = oui, 2 = non
boardRégénérer le tableau partagé et la page de ce dépôt. --mirror rend également les docs git configurés dans le plan
whoamiImprimer l'ID de cette exécution et ses baux détenus
setupÉcrire l'instantané généré du câblage de ce projet, pour que les agents le lisent
adoptInspecter un projet existant et proposer une configuration — n'écrit rien
mergeAtterrir cette branche : cible locale fast-forwardée, conflits vérifiés avant que quoi que ce soit ne soit touché, journal de fusion écrit, le bail --key libéré. --summary, --dry-run, --push
mergesCe qui a atterri pendant que vous étiez sur votre branche. --all inclut la queue compactée
checkValider toute la configuration de bout en bout. Non nul quand ce n'est pas sain
scaffold [--full]Créer uniquement ce qui manque, jamais une ligne sur ce qui existe. --full initialise également le registre de questions, l'index, le tableau de dépendances, le modèle de données avec son registre d'entités, et la porte des docs
finish [--gates]Le travail est-il terminé — chaque dépôt propre, poussé et pointé, aucun bail laissé détenu. check répond si le projet est câblé correctement ; celui-ci répond si vous avez terminé

$SKILL_DIR est le propre répertoire de cette compétence : ${CLAUDE_PLUGIN_ROOT}/skills/agent-sync sous le plugin Claude Code, ~/.agents/skills/agent-sync ailleurs. Résolvez-le une fois par session et réutilisez-le — ne devinez pas un chemin. Chaque commande lit .claude/agent-sync.json depuis la racine du projet et n'a besoin d'aucun argument au-delà de ceux listés.

Une identité par session, et comment elle est décidée

Un bail n'est un bail que si deux agents obtiennent deux identités. L'ordre compte ici et les deux extrémités ont mordu : dériver l'ID de CLAUDE_SESSION_ID seul a donné une session deux identités — elle acquiert comme une et a été refusée par sa propre garde comme l'autre — et garder un ID par checkout a donné deux sessions une identité, ce qui est pire. La seconde est silencieuse : les deux sessions acquièrent, les deux sont gardées, et release prend un bail que l'appelant n'a jamais eu.

L'ordre est : AGENT_SYNC_RUN_ID · CLAUDE_SESSION_ID · la session qui a démarré ce shell · partagé.

Le troisième compte car une simple commande shell n'a pas d'ID de session alors qu'un hook en a. Donc SessionStart timbre .agent-sync/sessions/<PID du CLI> avec la session qu'il connaît, et une commande ultérieure se trouve en remontant sa propre arborescence de processus jusqu'à un PID timbré. Pourquoi cela et pas l'analyse de la ligne de commande : references/earned-rules.md.

Quand aucun des quatre ne peut être établi, l'exécution le dit — "cette identité est partagée avec toute autre session dans ce checkout" — plutôt que de présenter une entrée partagée comme une séparation.

Revendication — la forme qui compte

acquire → faire le travail → release

Ne sautez jamais release, y compris en cas d'échec : un bail abandonné bloque la tâche jusqu'à ce que son TTL expire, et le prochain agent ne peut pas distinguer "en cours" de "planté il y a une heure".

Le bail n'est pas la revendication. Le bail dit qui le détient maintenant et expire ; la revendication durable est l'étiquette dans git, écrite par acquire et effacée par release. Un fait, un foyer — n'inventez pas un troisième endroit qui enregistre la propriété.

Lisez references/lease-protocol.md avant de changer l'acquisition, l'expiration, le vol ou l'allocation d'ID.

Fichiers gardés

La configuration liste les fichiers de registre que plusieurs agents écrivent. Avant d'en modifier un :

python3 "$SKILL_DIR/scripts/agent_sync.py" guard docs/DECISIONS.md

Le code de sortie 2 concerne cette exécution : elle ne détient aucun bail — pas que quelqu'un d'autre détienne ce fichier. Un bail couvre tous les fichiers gardés ; détenez-en un ou n'en écrivez aucun. Un refus nomme l'autre exécution et sa clé, car "r-x détient un bail" à côté d'un chemin se répète comme "r-x détient ce fichier". N'éditez pas quand même, et ne "corrigez pas juste une ligne" — une décision écrasée ressemble exactement à une décision.

Le hook PreToolUse de Claude Code exécute ceci pour vous. Ailleurs, rien ne le fait.

Réserver un ID

Lire une ligne "Prochain ID libre" n'est pas la réserver — deux agents lisent le même numéro et les deux l'utilisent.

python3 "$SKILL_DIR/scripts/agent_sync.py" reserve DEC   # → DEC-0216

L'allocation est positionnelle sur le journal fusionné — chaque fragment, jamais seulement celui de cette exécution — donc chaque agent calcule la même réponse. Réservé et non écrit dans git ? release-id le retourne, ou le nombre est un trou que le tableau signale comme une fuite.

Rien dans un journal n'est jamais modifié ou supprimé

Les journaux sont rejoués dans l'ordre, donc une modification réécrit silencieusement une conclusion sur laquelle d'autres agents ont déjà agi. Corrigez en ajoutant : libérez un bail, release-id un ID inutilisé, supersédez une entrée de réalisé erronée par une ultérieure. Les pages générées sont la seule exception, et celle qui a perdu sa marque agent-sync:generated

…

[tronqué pour la traduction]

Skills associés