O que é MonoSpecs: por que é uma atualização e extensão do OpenSpec
O que é MonoSpecs: por que é uma atualização e extensão do OpenSpec
Quando um ecossistema de produtos cresce para 40+ repositórios Git independentes, onde colocar as “especificações”? Este artigo discute as duas etapas que o HagiCode seguiu na governança de multi-repositórios: primeiro mover o OpenSpec para o repositório principal, depois desenvolver o MonoSpecs como uma solução de gerenciamento de multi-repositórios. Na verdade não é nada demais, só passamos por alguns problemas e queríamos documentá-los.
Contexto
Quem já trabalhou em produtos um pouco maiores provavelmente teve essa experiência — no começo o código estava em um único repositório, organizado, tudo tranquilo; depois frontend, backend, desktop, documentação, site oficial, ferramentas de build foram se separando em repositórios independentes, e o número de repositórios crescia rapidamente, como ervas daninhas, impossível de conter. Depois, quando você quer escrever um “documento de especificação” para uma funcionalidade que atravessa vários repositórios, de repente não sabe onde escrever, como dizer, é meio como a mesada da infância, como simplesmente desapareceu.
Nosso próprio HagiCode é um ecossistema de produtos composto por 40+ repositórios Git independentes. No início, colocamos o diretório openspec/ do OpenSpec diretamente no sub-repositório de backend hagicode-core, pensando que, afinal, o backend é o núcleo, seria o lugar mais estável. O resultado foi que, à medida que os repositórios eram divididos, essa solução expôs uma série de problemas de dor de cabeça. Afinal, no mundo do código, as coisas nunca ficam estáveis só porque você “pensa que ficam”.
O primeiro ponto de dor: especificações presas em um único sub-repositório. Se uma funcionalidade afeta tanto o frontend web quanto o backend hagicode-core, tenho que escrever a proposta no hagicode-core e depois ir para outros sub-repositórios executar as alterações de código. A qual repositório a proposta deve pertencer se torna uma controvérsia em si.
O segundo ponto de dor: sub-repositórios não são puros. Cada sub-repositório carrega seu próprio openspec/, misturando documentos de especificação com código do produto. Alguém clona seu repositório frontend e acaba trazendo de volta um monte de documentos de proposta de backend, completamente confuso.
O terceiro ponto de dor: Agentes de IA têm dificuldade em entender as relações entre repositórios. Os sub-repositórios são independentes uns dos outros, sem uma “lista” legível por máquina que diga à IA: de quais repositórios este produto é composto, cada um é responsável pelo que, qual é editável, qual é apenas uma referência de leitura.
O quarto ponto de dor: alto custo de edição entre repositórios. Para alterar uma especificação, primeiro é necessário fazer cd no submódulo correspondente, caminhos pulando de um lado para o outro, uma carga mental de colaboração extremamente alta.
Foi nesse contexto que primeiro fizemos uma “OpenSpec Monorepo Migration”, movendo as especificações dos sub-repositórios para o diretório raiz do monorepo. E, por cima disso, desenvolvemos o MonoSpecs como uma solução de gerenciamento de multi-repositórios. Entender a relação progressiva entre essas duas etapas é a chave para entender “por que monospec é uma atualização e extensão de openspec”.
Sobre o HagiCode
A solução compartilhada neste artigo vem da nossa experiência prática no projeto HagiCode. O HagiCode é um projeto de assistente de código de IA, com muitos repositórios e colaboração frequente entre linguagens. Essa complexidade estrutural nos forçou a tornar sólidas as duas coisas: “especificações” e “governança de repositórios”. A solução MonoSpecs foi refinada pouco a pouco nessa prática de multi-repositórios, na verdade não há nada genial, apenas demos alguns passos a mais.
OpenSpec resolve “como escrever e evoluir especificações”
Para esclarecer a relação entre os dois, é preciso separar o que cada um faz.
O OpenSpec é essencialmente um fluxo de trabalho de gerenciamento de mudanças dirigido por especificações. Sua saída principal é assim:
openspec/├── specs/ # Especificações de recursos atualmente em vigor (um spec.md por recurso)├── changes/ # Propostas em andamento│ └── archive/ # Propostas históricas arquivadas└── project.mdEle responde à pergunta: uma mudança deve passar pelo ciclo de vida de proposta (proposal), design (design), tarefas (tasks), arquivo (archive), e ao arquivar, os deltas são mesclados nas especificações. Este mecanismo em si é independente de “quantos repositórios existem, onde estão, quem os gerencia”, ele se preocupa apenas com como os arquivos de especificação são organizados.
Através de uma proposta de migração, movemos 82+ arquivos de especificação originalmente dispersos em hagicode-core/openspec/ para o openspec/ no diretório raiz do monorepo, tornando todas as especificações visíveis em um único lugar e com controle de versão unificado.
Mas esta migração, em última análise, foi apenas “mover os arquivos de especificação”, e não respondeu a uma questão mais fundamental: de quais sub-repositórios este monorepo é composto? Qual é a relação entre esses sub-repositórios? É aí que o MonoSpecs entra para preencher essa lacuna.
MonoSpecs resolve “como gerenciar os multi-repositórios em si”
O núcleo do MonoSpecs é um arquivo de manifesto legível por máquina: .hagicode/monospecs.yaml. Ele faz quatro coisas que o OpenSpec não aborda em absoluto.
Primeira: declarar o manifesto de sub-repositórios. O path, url, displayName, icon, tags de cada repositório, e se deve ser colapsado em “More”, tudo escrito em um YAML, claro de uma vez.
Segunda: dirigir o script de clone. scripts/clone-repos.mjs lê diretamente este YAML, faz git clone em lote, não mais codificando a lista de repositórios. Adicionar um repositório só requer adicionar uma linha no YAML, o script não precisa de alterações.
Terceira: fornecer contexto de estrutura de projeto para IA/IDE. Combinado com AGENTS.md, o Agente de IA pode ver imediatamente qual repositório é editável, qual é apenas referência, qual é a stack tecnológica.
Quarta: ancorar os produtos do OpenSpec no repositório principal. As especificações não são mais dispersas pelos sub-repositórios, mas reunidas no openspec/ no diretório raiz do repositório principal, mantendo os sub-repositórios puros.
Dois significados, não confunda
No guia oficial do MonoSpecs, um ponto muito confuso é claramente destacado: o MonoSpecs tem, na verdade, dois significados.
Um é o camada do sistema de configuração, referindo-se ao próprio arquivo de configuração .hagicode/monospecs.yaml, e seus mecanismos de carregamento, validação e cache associados.
Outro é o camada do tipo de repositório, referindo-se a um padrão de organização de repositórios “repositório principal + múltiplos sub-repositórios + especificações centralizadas”. Quando dizemos que um projeto “é um projeto MonoSpecs”, significa que ele adota essa estrutura.
Essas duas camadas sobrepostas constituem o MonoSpecs completo. Muitas pessoas ao entrar em contato pela primeira vez veem apenas a camada do arquivo YAML, pensando que o MonoSpecs é apenas uma lista de configuração, mas seu valor está mais na segunda camada — um paradigma claro de colaboração de multi-repositórios. Na verdade, coisas bonitas geralmente não estão na primeira olhada, precisa olhar mais algumas vezes.
Por que é “atualização e extensão”
Colocando os dois lado a lado, a relação fica clara:
| Dimensão | OpenSpec | MonoSpecs |
|---|---|---|
| Foco | Conteúdo e ciclo de vida de arquivos de especificação | Estrutura organizacional e manifesto de repositórios |
| Produto principal | openspec/specs/*/spec.md | .hagicode/monospecs.yaml |
| Depende do outro | Não depende do MonoSpecs | Depende do OpenSpec, reutiliza seu openspec/ para gerenciamento de mudanças |
| Dores resolvidas | Como escrever e evoluir especificações | Como declarar multi-repositórios, como clonar, como a IA entende |
| Escopo de ação | Pode ser usado em qualquer repositório | Projetado especificamente para estrutura de multi-repositórios “um principal, vários filhos” |
Em suma, o MonoSpecs não substitui o OpenSpec, mas adiciona uma camada de “governança de repositórios” sobre ele. Usa monospecs.yaml para descrever a topologia de repositórios, usa openspec/ centralizado para desacoplar especificações dos sub-repositórios, e usa commit_when_archive para fazer o arquivamento automaticamente commitar no repositório principal.
Se usar uma analogia: OpenSpec fornece “sintaxe de mudanças”, MonoSpecs fornece “semântica de multi-repositórios”. O primeiro é o pré-requisito do segundo, o segundo é a extensão do primeiro. Todos os caminhos levam a Roma, só que desta vez, o caminho é um pouco mais longo do que se imaginava.
Como implementar: quatro passos
Primeiro passo: estabelecer o repositório principal e o arquivo de configuração
Coloque o arquivo de configuração no diretório raiz do monorepo, declarando todos os sub-repositórios. Tomando nosso próprio projeto como exemplo, a estrutura é mais ou menos assim:
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: "Documentação" tags: [docs, astro, starlight] ui: collapseToMore: true # Colapsar em "More" na UIAlguns campos merecem atenção especial:
pathé o caminho local relativo à raiz do repositório principal, também é a chave única de cada registro.urlé o endereço remoto Git, o script de clone depende dele para puxar o código.displayName/icon/tagssó afetam a exibição da UI e o contexto da IA, não afetam o comportamento de clone.commit_when_archive: truefaz com que as propostas do OpenSpec sejam automaticamente commitadas no repositório principal ao arquivar.
Segundo passo: mover o OpenSpec para o diretório raiz do repositório principal
Comparação antes e depois da migração:
Antes da migração (specs presas em sub-repositório) Depois da migração (specs centralizadas no repositório principal)hagicode-core/ . (raiz do repositório principal)└── openspec/ ├── .hagicode/monospecs.yaml └── specs/ (82+ specs) ├── openspec/ │ ├── specs/ (gerenciamento centralizado) │ └── changes/ └── repos/ ├── hagicode-core/ (puro, sem openspec) ├── web/ └── docs/Daqui em diante, os sub-repositórios não carregam mais openspec/, o repositório principal torna-se a única fonte de verdade das especificações. Este passo parece simples, mas os benefícios são muito reais — qualquer engenheiro parado no diretório raiz do repositório principal pode ver todas as especificações de todo o ecossistema de produtos.
Terceiro passo: fazer o script de clone ler a configuração em vez de codificar
A lógica principal de scripts/clone-repos.mjs é ler o YAML e clonar item por item:
const CONFIG_PATH = path.join(__dirname, '..', '.hagicode', 'monospecs.yaml');// Analisar o array repositories// Executar git clone <url> <path> para cada item// Se o diretório de destino já existe, pular ou fazer git pullAo adicionar um novo repositório, só precisa adicionar uma linha no YAML, não precisa tocar no script. Essa pequena mudança economiza inúmeras discussões de “esquecer de sincronizar a lista de repositórios”. Afinal, quem quer trabalho repetido?
Quarto passo: o backend fornece uma camada de serviço MonoSpecs unificada
Se não abstrairmos uma camada, a lógica de análise de configuração facilmente se espalha pelos cantos de GitAppService, ProjectAppService. O HagiCode extraiu IMonoSpecsService no módulo ClaudeHelper, expondo um conjunto de capacidades claras:
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);}Este serviço é responsável por carregar, validar e armazenar em cache a configuração, e fornecer a capacidade de “inicializar modelo mínimo” — gerar com um clique o esqueleto de monospecs.yaml, repos/, openspec/changes/archive/, openspec/specs/ para um projeto vazio, e automaticamente completar o .gitignore. O cache, como a memória, quando lembrado, na próxima vez não precisa se esforçar para pensar.
Algumas armadilhas na prática
Inicializar um novo projeto MonoSpecs
Depois de chamar InitializeManagementDocumentAsync, a seguinte estrutura aparecerá no disco:
my-project/├── .gitignore # Adiciona regra de ignorar repos/ (idempotente, não adiciona repetidamente)├── .hagicode/│ └── monospecs.yaml # Modelo mínimo: version / commit_when_archive / repositories: []├── openspec/│ ├── changes/archive/│ └── specs/└── repos/ # Diretório vazio, esperando cloneAlguns limites a observar aqui, todos extraídos das especificações:
- Idempotente: Diretórios
repos/,openspec/existentes serão preservados, não causarão erro. - Não sobrescrever: Se
monospecs.yamljá existe e pode ser analisado normalmente, a inicialização não o alterará, apenas complementará regras.gitignorefaltantes e diretórios openspec. - Rejeitar configuração suja:
monospecs.yamlexistente mas não analisável será rejeitado diretamente, retornando informações de erro diagnosticáveis, nunca sobrescreverá. - Não escaneia automaticamente: A inicialização não tomará a iniciativa de escanear diretórios do disco em entradas de repositório,
repositoriespadrão vazio, precisa ser preenchido manualmente ou através da UI.
Armadilha de migração de localização do arquivo de configuração
Historicamente, monospecs.yaml foi colocado no diretório raiz do projeto, depois forçado a migrar para .hagicode/monospecs.yaml. Isso está muito claro na especificação:
O
monospecs.yamlno diretório raiz não é mais detectado, nem serve como fallback de compatibilidade. O script de clone só reconhece.hagicode/monospecs.yaml.
Portanto, ao atualizar projetos antigos, é necessário executar manualmente mv monospecs.yaml .hagicode/monospecs.yaml, não há caminho de compatibilidade silenciosa. À primeira vista parece um pouco impessoal, mas pensando bem, é para eliminar completamente a ambiguidade de “ambos os locais podem entrar em vigor” — essa ambiguidade, uma vez existente, pode enlouquecer as pessoas ao solucionar problemas, afinal, ninguém quer procurar respostas entre dois arquivos.
Validação de salvamento: não escreva configurações inválidas
Antes de escrever de volta através de SaveManagementDocumentAsync, o serviço fará validação de nível de campo. Alguns cenários típicos de rejeição:
- Dois itens de repositório com
pathduplicado → Rejeita, retorna campo conflitante. - Qualquer item faltando
path→ Rejeita, retorna erro de campo obrigatório. urlnão vazio mas não é uma URL absoluta válida → Rejeita.
Somente após a validação passar é serializado para YAML e gravado no disco, enquanto invalida o cache de configuração do caminho do projeto, garantindo que a próxima leitura obtenha o conteúdo mais recente. Este passo parece trivial, mas pode evitar inúmeros tíquetes de “por que minhas alterações de configuração não entraram em vigor”, afinal, com muitos desses tíquetes, ninguém aguenta.
Modo workspace vs modo manual de repositories
O arquivo de configuração suporta duas maneiras de derivar a lista de repositórios.
Uma é o modo manual de repositories, listando diretamente cada repositório no YAML, documento de gerenciamento marcado como editável.
Outra é o modo workspace, declarando um arquivo .code-workspace, do qual deriva a lista de repositórios. Neste modo, o documento de gerenciamento é marcado como somente leitura, proibindo alterar diretamente o array de repositórios, só pode alterar campos de nível superior suportados.
Nosso próprio HagiCode Mono comentou o modo workspace, adotando o modo manual. A razão é simples: o modo manual pode controlar refinadamente o icon e tags de cada repositório, o efeito de exibição da UI é mais controlável. Como dizer, coisas que podemos controlar, o coração sempre fica mais tranquilo.
Sugestões práticas para Agentes de IA
Agora que a programação com IA está se tornando cada vez mais popular, a solução MonoSpecs na verdade tem um valor implícito: ela fornece um mapa de projeto estruturado para a IA.
Na colaboração de multi-repositórios, AGENTS.md e monospecs.yaml são dois contextos chave para a IA. O fluxo de trabalho sugerido é este:
- Primeiro leia
monospecs.yamlpara obter a topologia de repositórios, entenda quem é editável, quem é apenas referência. - Depois leia “Active Edit Scope” do
AGENTS.mdraiz, confirme o escopo de modificação atualmente permitido. - Para mudanças entre repositórios, escreva propostas uniformemente no
openspec/changes/do repositório principal raiz, não inicie openspec separado em cada sub-repositório.
Esta convenção permite que a IA entenda estavelmente a divisão de trabalho “repositório principal gerencia especificações, sub-repositórios gerenciam código”, e não escreva acidentalmente especificações nos sub-repositórios — já caímos nesta armadilha várias vezes. Na verdade, não é culpa da IA, afinal, sub-repositórios e repositório principal parecem tão parecidos, quem pode distinguir à primeira vista?
Conclusão
Resumindo em uma frase: OpenSpec define “como escrever mudanças”, MonoSpecs define “como organizar repositórios”.
O primeiro é a base sintática do segundo, o segundo estende o primeiro do contexto de repositório único para o contexto de multi-repositórios, e converge em um manifesto YAML a topologia de repositórios, o fluxo de clone, o contexto da IA, e a atribuição de especificações de uma só vez. Este é o verdadeiro significado de “monospec é uma atualização e extensão de openspec” — não substituição, mas adicionar uma camada de semântica de multi-repositórios sobre ele.
Se você também está fazendo um produto de multi-repositórios de escala semelhante, considere se essas duas camadas estão bem estabelecidas. Especificações escritas lindamente, sem uma governança de repositórios clara para sustentar, no final ainda se tornarão uma bagunça…
Referências
- Site oficial do HagiCode
- Repositório GitHub HagiCode-org/site
- Documentação de fluxo de trabalho do OpenSpec
- Specs relacionadas ao MonoSpecs:
monospecs-guide,monospecs-repository-config,monospec-config-management
Conclusão
Em torno de “O que é MonoSpecs: por que é uma atualização e extensão do OpenSpec”, uma maneira mais sólida de avançar é primeiro testar gradualmente configurações chave, limites de dependência e caminhos de implementação, depois complementar detalhes de otimização.
Quando objetivos, passos e pontos de aceitação estão claros, esse tipo de solução geralmente pode entrar em entrega real de forma mais suave.
开始使用 HagiCode
一次安装,几分钟上手
HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。