MonoSpecs qu'est-ce que c'est : pourquoi c'est une évolution et une extension supplémentaire d'OpenSpec
MonoSpecs qu’est-ce que c’est : pourquoi c’est une évolution et une extension supplémentaire d’OpenSpec
Quand un système de produits s’étend à 40+ dépôts Git indépendants, où doivent se trouver les “spécifications” ? Cet article discute des deux étapes parcourues par HagiCode dans la gouvernance multi-dépôts : d’abord remonter OpenSpec au dépôt principal, puis développer sur cette base MonoSpecs, une solution de gestion multi-dépôts. Ce n’est pas grand-chose en fait, juste quelques obstacles rencontrés que je veux noter.
Contexte
Ceux qui ont travaillé sur des produits un peu plus grands ont probablement vécu cette expérience - au début, le code est dans un seul dépôt, bien organisé, tout va bien ; plus tard, frontend, backend, desktop, site de documentation, site officiel, outils de construction deviennent chacun des dépôts indépendants, le nombre de dépôts augmente rapidement, comme des mauvaises herbes impossibles à arrêter. Plus tard, quand vous voulez écrire un “document de spécification” pour une fonctionnalité qui traverse les dépôts, vous ne savez soudain plus où l’écrire, comment dire, c’est un peu comme l’argent de poche quand on était enfant, comment ça a disparu soudainement.
Notre propre HagiCode est justement un système de produits composé de 40+ dépôts Git indépendants. Au début, nous avons directement mis le répertoire openspec/ d’OpenSpec dans le sous-dépôt backend hagicode-core, en pensant que puisque le backend est le cœur, le mettre ici serait le plus stable. Résultat : les dépôts se sont divisés de plus en plus, et cette solution a révélé une série de problèmes qui donnent mal à la tête. Après tout, le monde du code ne devient jamais stable juste parce que vous “pensez que c’est stable”.
Premier point de douleur : les specs sont piégées dans un seul sous-dépôt. Si une fonctionnalité affecte à la fois le frontend web et le backend hagicode-core, je dois écrire la proposition dans hagicode-core, puis aller dans d’autres sous-dépôts pour exécuter les modifications de code. À quel dépôt la proposition devrait-elle appartenir est devenu en soi une controverse.
Deuxième point de douleur : les sous-dépôts ne sont pas purs. Chaque sous-dépôt porte son propre openspec/, les documents de spécification et le code du produit sont mélangés. Quelqu’un clone votre dépôt frontend et ramène une pile de documents de proposition backend, complètement perdu.
Troisième point de douleur : les AI Agents ont du mal à comprendre les relations entre les dépôts. Les sous-dépôts sont indépendants les uns des autres, il n’y a pas de “liste” lisible par machine pour dire à l’IA : de quels dépôts ce produit est composé, de quoi chacun est responsable, lequel est éditable, lequel est une référence en lecture seule.
Quatrième point de douleur : coût élevé d’édition inter-dépôts. Pour modifier une spec, il faut d’abord cd dans le sous-module correspondant, les chemins sautent de partout, la charge mentale de collaboration est énorme.
C’est dans ce contexte que nous avons d’abord fait une “OpenSpec Monorepo Migration”, en remontant les specs des sous-dépôts vers le répertoire racine du monorepo. Puis, sur cette base, nous avons développé MonoSpecs, cette solution de gestion multi-dépôts. Comprendre la relation progressive entre ces deux étapes est la clé pour comprendre “pourquoi on dit que monospec est une évolution et une extension supplémentaire d’openspec”.
À propos de HagiCode
La solution partagée dans cet article provient de notre expérience pratique dans le projet HagiCode. HagiCode est un projet d’assistant de code IA, avec un grand nombre de dépôts et une collaboration inter-langues fréquente, cette complexité structurelle nous oblige à faire un travail solide sur les “spécifications” et la “gouvernance des dépôts”. La solution MonoSpecs a été affinée petit à petit dans cette pratique multi-dépôts, il n’y a pas de coup de génie, juste quelques pas de plus.
OpenSpec résout “comment écrire et faire évoluer les spécifications”
Pour clarifier la relation entre les deux, il faut d’abord regarder ce que chacun fait.
OpenSpec est essentiellement un workflow de gestion des changements piloté par des specs. Ses produits principaux ressemblent à ceci :
openspec/├── specs/ # Spécifications de capacités actuellement en vigueur (une spec.md par capacité)├── changes/ # Propositions en cours│ └── archive/ # Propositions historiques archivées└── project.mdIl répond à la question : un changement doit passer par le cycle de vie de proposition (proposal), conception (design), tâches (tasks), archivage (archive), et lors de l’archivage, fusionner les deltas dans les specs. Ce mécanisme lui-même est indépendant de “combien de dépôts, où ils sont, qui les gère”, il se soucie seulement de l’organisation des fichiers spec.
Nous avons fait une proposition de migration pour remonter les 82+ fichiers spec dispersés dans hagicode-core/openspec/ vers le openspec/ à la racine du monorepo, rendant toutes les spec visibles en un seul endroit et sous contrôle de version unifié.
Mais cette migration était fondamentalement juste “déménager les fichiers spec”, et n’a pas répondu à une question plus fondamentale : de quels sous-dépôts ce monorepo est-il composé ? Quelles sont les relations entre ces sous-dépôts ? C’est ce que MonoSpecs doit combler.
MonoSpecs résout “comment gérer les multi-dépôts eux-mêmes”
Le cœur de MonoSpecs est un fichier manifeste lisible par machine : .hagicode/monospecs.yaml. Il fait quatre choses qu’OpenSpec ne touche pas du tout.
Première : déclarer la liste des sous-dépôts. Le path, l’url, le displayName, l’icon, les tags de chaque dépôt, et s’il doit être plié dans “More”, tout est écrit dans un YAML, clair d’un coup d’œil.
Deuxième : piloter le script de clone. scripts/clone-repos.mjs lit directement ce YAML, fait des git clone en lot, ne hardcode plus la liste des dépôts. Ajouter un dépôt demande juste une ligne dans le YAML, le script n’a besoin d’aucune modification.
Troisième : fournir le contexte de structure de projet à l’AI/IDE. Avec AGENTS.md, l’AI Agent peut voir d’un coup d’œil quel dépôt est éditable, lequel est reference-only, quelle est la stack technique.
Quatrième : ancrer les produits d’OpenSpec au dépôt principal. Les specs ne sont plus dispersées dans les sous-dépôts, mais sont unifiées dans le openspec/ à la racine du dépôt principal, les sous-dépôts restent donc purs.
Deux niveaux de signification, ne les confondez pas
Dans le guide officiel de MonoSpecs, un point très facile à confondre est clairement indiqué : MonoSpecs a en fait deux niveaux de signification.
Un niveau est le niveau du système de configuration, qui fait référence au fichier de configuration .hagicode/monospecs.yaml lui-même, ainsi qu’à ses mécanismes de chargement, validation et cache associés.
L’autre niveau est le niveau du type de dépôt, qui fait référence à un mode d’organisation de dépôts “dépôt principal + plusieurs sous-dépôts + specs centralisés”. Quand nous disons qu’un projet “est un projet MonoSpecs”, cela signifie qu’il adopte cette structure.
Ces deux niveaux superposés forment le MonoSpecs complet. Beaucoup de gens au premier contact ne voient que le niveau du fichier YAML, pensant que MonoSpecs est juste une liste de configuration, mais sa valeur est davantage dans le deuxième niveau - un paradigme clair de collaboration multi-dépôts. En fait, les belles choses ne sont souvent pas au premier coup d’œil, il faut les regarder plusieurs fois.
Pourquoi dire “évolution et extension”
En mettant les deux côte à côte pour comparaison, la relation devient claire :
| Dimension | OpenSpec | MonoSpecs |
|---|---|---|
| Focus | Contenu et cycle de vie des fichiers spec | Structure organisationnelle et liste des dépôts |
| Produit principal | openspec/specs/*/spec.md | .hagicode/monospecs.yaml |
| Dépend de l’autre | Ne dépend pas de MonoSpecs | Dépend d’OpenSpec, réutilise son openspec/ pour la gestion des changements |
| Problèmes résolus | Comment écrire et faire évoluer les spécifications | Comment déclarer les multi-dépôts, comment cloner, comment l’IA comprend |
| Portée | Peut être utilisé dans n’importe quel dépôt | Conçu spécifiquement pour les structures multi-dépôts “un principal plusieurs sous” |
Pour le dire simplement, MonoSpecs ne remplace pas OpenSpec, mais ajoute une couche de “gouvernance des dépôts” au-dessus. Utiliser monospecs.yaml pour décrire la topologie des dépôts, utiliser openspec/ centralisé pour découpler les specs des sous-dépôts, utiliser commit_when_archive pour que l’archivage soit automatiquement commité dans le dépôt principal.
Si on fait une analogie : OpenSpec fournit la “syntaxe de changement”, MonoSpecs fournit la “sémantique multi-dépôts”. Le premier est la précondition du second, le second est l’extension du premier. Tous les chemins mènent à Rome, mais cette fois, le chemin est un peu plus long qu’imaginé.
Comment mettre en œuvre : quatre étapes
Première étape : établir le dépôt principal et le fichier de configuration
Placez le fichier de configuration à la racine du monorepo et déclarez tous les sous-dépôts. Avec notre propre projet comme exemple, la structure est à peu près comme ceci :
version: "1.0"commit_when_archive: true
repositories:- path: "repos/web" url: "https://github.com/HagiCode-org/web.git" displayName: "Frontend" tags: [frontend, react, pcode-client]
- path: "repos/hagicode-core" url: "https://github.com/newbe36524/pcode" displayName: "Backend" tags: [backend, dotnet, orleans]
- path: "repos/docs" url: "https://github.com/HagiCode-org/docs.git" displayName: "Documentation" tags: [docs, astro, starlight] ui: collapseToMore: true # Plier dans "More" dans l'UIIl y a quelques champs à particulièrement attention :
pathest le chemin local relatif à la racine du dépôt principal, et aussi la clé unique de chaque enregistrement.urlest l’adresse Git distante, le script de clone s’y appuie pour tirer le code.displayName/icon/tagsn’affectent que l’affichage UI et le contexte IA, n’affectent pas le comportement de clone.commit_when_archive: truefait que les propositions OpenSpec sont automatiquement commitées dans le dépôt principal lors de l’archivage.
Deuxième étape : remonter OpenSpec à la racine du dépôt principal
Comparaison avant et après migration :
Avant migration (specs piégées dans sous-dépôt) Après migration (specs centralisées dans dépôt principal)hagicode-core/ . (racine dépôt principal)└── openspec/ ├── .hagicode/monospecs.yaml └── specs/ (82+ specs) ├── openspec/ │ ├── specs/ (gestion centralisée) │ └── changes/ └── repos/ ├── hagicode-core/ (pur, sans openspec) ├── web/ └── docs/Les sous-dépôts ne portent plus openspec/, le dépôt principal devient la seule source de vérité des specs. Cette étape semble simple, mais apporte des bénéfices très réels - n’importe quel ingénieur debout à la racine du dépôt principal peut voir toutes les spécifications de tout le système de produits.
Troisième étape : faire lire la configuration au script de clone plutôt que hardcoder
La logique principale de scripts/clone-repos.mjs est de lire le YAML et cloner un par un :
const CONFIG_PATH = path.join(__dirname, '..', '.hagicode', 'monospecs.yaml');// Parser le tableau repositories// Pour chaque entrée exécuter git clone <url> <path>// Si le répertoire cible existe déjà, sauter ou git pullQuand on ajoute un dépôt, il suffit d’ajouter une ligne dans le YAML, sans toucher au script. Ce petit changement économise d’innombrables discussions sur “oublier de synchroniser la liste des dépôts”. Après tout, qui veut faire du travail répétitif ?
Quatrième étape : le backend fournit une couche de service MonoSpecs unifiée
Sans extraire une couche d’abstraction, la logique de parsing de configuration se disperse facilement dans GitAppService, ProjectAppService et divers coins. HagiCode a extrait IMonoSpecsService dans le module ClaudeHelper, exposant un ensemble clair de capacités :
public interface IMonoSpecsService{ Task<MonoSpecsConfigDto> GetConfigAsync(string projectPath); Task<List<RepositoryInfoDto>> GetSubRepositoriesAsync(string projectPath); Task<MonoSpecsDataDto> GetMonoSpecsDataAsync(string projectPath); Task<MonoSpecsManagementDto> GetManagementDocumentAsync(string projectPath); Task<MonoSpecsManagementDto> InitializeManagementDocumentAsync(string projectPath); Task ValidateManagementDocumentAsync(string projectPath, UpdateMonoSpecsManagementRequestDto request); Task SaveManagementDocumentAsync(string projectPath, UpdateMonoSpecsManagementRequestDto request);}Ce service est responsable du chargement, de la validation, du cache de la configuration, et fournit la capacité “d’initialiser le modèle minimal” - générer en un clic monospecs.yaml, repos/, openspec/changes/archive/, openspec/specs/ pour un projet vide, et compléter automatiquement .gitignore. Le cache, c’est comme la mémoire, une fois mémorisé, la prochaine fois on n’a plus besoin de réfléchir.
Quelques pièges dans la pratique
Initialiser un nouveau projet MonoSpecs
Après avoir appelé InitializeManagementDocumentAsync, cette structure apparaîtra sur le disque :
my-project/├── .gitignore # Nouvelle règle d'ignore repos/ (idempotent, pas d'ajout en double)├── .hagicode/│ └── monospecs.yaml # Modèle minimal : version / commit_when_archive / repositories: []├── openspec/│ ├── changes/archive/│ └── specs/└── repos/ # Répertoire vide, attendant le cloneIl y a quelques frontières à attentionner, toutes extraites de la spec :
- Idempotent : les répertoires
repos/,openspec/existants seront préservés, pas d’erreur. - Pas de surcharge : si
monospecs.yamlexiste déjà et peut être analysé normalement, l’initialisation ne le touchera pas, seulement compléter les règles.gitignoremanquantes et les répertoires openspec. - Refus des configurations sales :
monospecs.yamlexistant mais impossible à analyser sera directement refusé, retournant une information d’erreur diagnostique, absolument pas surchargé. - Pas de scan automatique : l’initialisation ne scannera pas automatiquement les répertoires du disque en entrées de dépôt,
repositoriesest vide par défaut, doit être rempli manuellement ou via l’UI.
Piège de migration de l’emplacement du fichier de configuration
Historiquement, monospecs.yaml était placé à la racine du projet, puis forcé à migrer vers .hagicode/monospecs.yaml. Ce point est très clair dans la spec :
monospecs.yamlà la racine n’est plus détecté, ni utilisé comme repli de compatibilité. Le script de clone ne reconnaît que.hagicode/monospecs.yaml.
Donc lors de la mise à niveau des anciens projets, il faut manuellement exécuter mv monospecs.yaml .hagicode/monospecs.yaml, aucun chemin de compatibilité silencieux. À première vue un peu impitoyable, mais réfléchissez bien, c’est pour éliminer complètement l’ambiguïté “les deux positions peuvent toutes deux prendre effet” - une fois cette ambiguïté existe, le dépannage peut rendre fou, après tout qui veut chercher des réponses entre deux fichiers ?
Validation de sauvegarde : n’écrivez pas de configuration invalide
Avant d’écrire via SaveManagementDocumentAsync, le service fera une validation au niveau des champs. Quelques scénarios typiques de refus :
- Deux entrées de dépôt avec
pathen double → refusé, retourne le champ en conflit. - N’importe quelle entrée sans
path→ refusé, retourne l’erreur champ obligatoire. urlnon vide mais pas une URL absolue valide → refusé.
Seulement après validation passée sera sérialisé en YAML et écrit sur disque, en invalidant le cache de configuration du chemin de projet, garantissant que la prochaine lecture obtient le contenu le plus récent. Cette étape semble triviale, mais peut éviter d’innombrables tickets “pourquoi mes changements de configuration ne prennent pas effet”, après tout trop de ces tickets, personne ne peut supporter.
Mode workspace vs mode repositories manuel
Le fichier de configuration supporte deux façons de dériver la liste des dépôts.
L’une est le mode repositories manuel, lister directement chaque dépôt dans le YAML, le document de gestion marqué comme éditable.
L’autre est le mode workspace, déclarer un fichier .code-workspace, qui dérive la liste des dépôts. Dans ce mode le document de gestion est marqué comme lecture seule, interdisant de réécrire directement le tableau de dépôts, seulement modifier les champs de niveau supérieur supportés.
Notre propre HagiCode Mono a commenté le mode workspace, adoptant le mode manuel. La raison est simple : le mode manuel peut contrôler finement l’icon et les tags de chaque dépôt, l’affichage UI est plus contrôlable. Comment dire, les choses qu’on peut maîtriser, le cœur est toujours plus tranquille.
Suggestions pratiques pour les AI Agents
Maintenant que la programmation IA est de plus en plus répandue, la solution MonoSpecs a en fait une valeur implicite : elle fournit à l’IA une carte de projet structurée.
Dans la collaboration multi-dépôts, AGENTS.md et monospecs.yaml sont deux contextes clés pour l’IA. Le workflow recommandé est le suivant :
- D’abord lire
monospecs.yamlpour obtenir la topologie des dépôts, clarifier qui est éditable, qui est reference-only. - Puis lire “Active Edit Scope” du
AGENTS.mdracine, confirmer la plage de modifications actuellement autorisée. - Les changements inter-dépôts écrivent des propositions uniformément dans
openspec/changes/à la racine du dépôt principal, ne démarrez pas d’openspec séparé dans chaque sous-dépôt.
Cette convention permet à l’IA de comprendre stablement la division “le dépôt principal gère les specs, les sous-dépôts gèrent le code”, sans écrire par erreur des specs dans les sous-dépôts - cette mauvaise opération nous a causé plusieurs problèmes. Ce n’est pas la faute de l’IA, après tout les sous-dépôts et le dépôt principal se ressemblent tellement, qui peut les distinguer d’un coup d’œil ?
Résumé
En un mot : OpenSpec définit “comment écrire les changements”, MonoSpecs définit “comment organiser les dépôts”.
Le premier est la base syntaxique du second, le second étend le premier du contexte de dépôt unique au contexte multi-dépôts, et converge en une seule fois la topologie des dépôts, le flux de clone, le contexte IA, et l’appartenance des specs avec une liste YAML. C’est la véritable signification de “monospec est une évolution et une extension supplémentaire d’openspec” - pas un remplacement, mais une couche de sémantique multi-dépôts au-dessus.
Si vous faites aussi des produits multi-dépôts d’échelle similaire, pensez si ces deux niveaux sont bien en place. Même si les spécifications sont écrites magnifiquement, sans une gouvernance claire des dépôts pour les soutenir, ça finira par devenir un chaos…
Références
- Site officiel HagiCode
- Dépôt GitHub HagiCode-org/site
- Documentation du workflow OpenSpec
- Specs liées à MonoSpecs :
monospecs-guide,monospecs-repository-config,monospec-config-management
Résumé
Autour de “MonoSpecs qu’est-ce que c’est : pourquoi c’est une évolution et une extension supplémentaire d’OpenSpec”, une façon plus sûre de progresser est d’abord faire fonctionner progressivement les configurations clés, les frontières de dépendances et les chemins de mise en œuvre, puis compléter les détails d’optimisation.
Quand les objectifs, les étapes et les points d’acceptation sont clairs, ce type de solution peut généralement entrer plus fluidement dans la livraison réelle.
开始使用 HagiCode
一次安装,几分钟上手
HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。