Ir al contenido

MonoSpecs ¿Qué es y por qué se dice que es una actualización y extensión de OpenSpec

Edita esta página
HagiCode for Windows Microsoft Store artwork
HagiCode for Windows is now on Microsoft Store
HagiCode for Windows is officially live on Microsoft Store. Windows users can install it directly from the storefront and stay on the store-managed update path. Open the listing and take a look.
Open Microsoft Store

MonoSpecs ¿Qué es y por qué se dice que es una actualización y extensión de OpenSpec

Cuando un sistema de productos se expande hasta tener 40+ repositorios Git independientes, ¿dónde deben ir las “especificaciones”? Este artículo habla sobre los dos pasos que HagiCode ha dado en la gestión de múltiples repositorios: primero subir OpenSpec al repositorio principal, y luego desarrollar MonoSpecs como un esquema de gestión de múltiples repositorios encima de eso. En realidad no es gran cosa, solo hemos pasado por algunos problemas y queríamos documentarlos.

Antecedentes

Quienes hayan trabajado en productos un poco más grandes probablemente hayan tenido esta experiencia: al principio el código estaba en un solo repositorio, ordenado y tranquilo; luego el frontend, backend, escritorio, sitio de documentación, sitio web oficial, herramientas de construcción se separaron en repositorios independientes, y el número de repositorios aumentaba como hierba salvaje, sin forma de detenerlo. Más adelante, cuando quieres escribir un “documento de especificación” para una función que abarca varios repositorios, de repente no sabes dónde escribirlo, ¿cómo explicarlo? Es un poco como el dinero de bolsillo de la infancia, ¿cómo desapareció de repente?

Nuestro propio HagiCode es exactamente un sistema de productos compuesto por 40+ repositorios Git independientes. Al principio, pusimos directamente el directorio openspec/ de OpenSpec en el sub-repositorio backend hagicode-core, pensando que como el backend es el núcleo, estaría más seguro allí. Pero a medida que se dividía más y más repositorios, este esquema expuso una serie de problemas dolorosos. Después de todo, el mundo del código nunca se hace estable solo porque tú “piensas que es seguro”.

El primer punto de dolor: los specs están atrapados en un solo sub-repositorio. Si una función afecta tanto al frontend web como al backend hagicode-core, tengo que escribir la propuesta en hagicode-core y luego ir a otros sub-repositorios para ejecutar cambios de código. A qué repositorio pertenece la propuesta se convirtió en una controversia en sí misma.

El segundo punto de dolor: los sub-repositorios no están puros. Cada sub-repositorio carga su propio openspec/, mezclando documentos de especificación con código de producto. Alguien clona tu repositorio frontend y resulta que trae un montón de documentos de propuestas backend, totalmente confundido.

El tercer punto de dolor: los Agentes de IA tienen dificultades para entender las relaciones entre repositorios. Cada sub-repositorio es independiente entre sí, no hay una “lista” legible por máquina que le diga a la IA: de qué repositorios se compone este producto, qué hace cada uno, cuál es editable y cuál es solo referencia de lectura.

El cuarto punto de dolor: el costo de edición entre repositorios es alto. Para cambiar un spec, primero debes cd al sub-módulo correspondiente, saltando de ruta en ruta, lo que crea una carga mental enorme para la colaboración.

Fue en este contexto donde primero hicimos una “OpenSpec Monorepo Migration”, subiendo los specs de los sub-repositorios al directorio raíz del monorepo. Y encima de eso, desarrollamos MonoSpecs como un esquema de gestión de múltiples repositorios. Entender esta relación progresiva es la clave para entender “por qué se dice que monospec es una actualización y extensión de openspec”.

Acerca de HagiCode

El esquema compartido en este artículo proviene de nuestra experiencia práctica en el proyecto HagiCode. HagiCode es un proyecto de asistente de código con IA, con muchos repositorios y colaboración frecuente entre lenguajes, esta complejidad estructural nos obligó a hacer sólidas tanto las “especificaciones” como la “gobernanza de repositorios”. El esquema MonoSpecs se fue puliendo poco a poco en esta práctica de múltiples repositorios, en realidad no hay nada brillante, solo dimos algunos pasos más.

OpenSpec resuelve “cómo escribir y evolucionar especificaciones”

Para explicar claramente la relación entre ambos, primero hay que separar lo que cada uno hace.

OpenSpec es esencialmente un flujo de trabajo de gestión de cambios basado en especificaciones. Su producto central se ve así:

openspec/
├── specs/ # Especificaciones de capacidades vigentes (un spec.md por capacidad)
├── changes/ # Propuestas en progreso
│ └── archive/ # Propuestas históricas archivadas
└── project.md

Responde la pregunta: un cambio debe pasar por el ciclo de vida de propuesta (proposal), diseño (design), tareas (tasks), archivo (archive), y al archivar fusionar los deltas en specs. Este mecanismo en sí mismo no tiene nada que ver con “cuántos repositorios hay, dónde están, quién los administra”, solo se preocupa de cómo se organizan los archivos spec.

A través de una propuesta de migración, subimos los 82+ archivos spec que originalmente estaban dispersos en hagicode-core/openspec/ al directorio raíz del monorepo openspec/, haciendo que todos los specs sean visibles en un solo lugar y con control de versiones unificado.

Pero esta migración, para ser honestos, solo fue “mover archivos spec”, no respondió a la pregunta más fundamental: ¿de qué sub-repositorios se compone exactamente este monorepo? ¿Cuál es la relación entre estos sub-repositorios? Eso es lo que MonoSpecs viene a complementar.

MonoSpecs resuelve “cómo gestionar múltiples repositorios”

El núcleo de MonoSpecs es un archivo de lista legible por máquina: .hagicode/monospecs.yaml. Hace cuatro cosas que OpenSpec no toca en absoluto.

La primera: declarar la lista de sub-repositorios. El path, url, displayName, icon, tags, si se colapsa en “More” de cada repositorio, todo escrito en un YAML, de un vistazo.

La segunda: impulsar el script de clonación. scripts/clone-repos.mjs lee directamente este YAML, hace git clone por lotes, ya no codifica la lista de repositorios. Para agregar un repositorio, solo agrega una línea en el YAML, el script necesita cero cambios.

La tercera: proporcionar contexto de estructura del proyecto a IA/IDE. Junto con AGENTS.md, el Agente de IA puede ver de un vistazo qué repositorio es editable, cuál es solo referencia, cuál es el stack tecnológico.

La cuarta: anclar el producto de OpenSpec al repositorio principal. Los specs ya no se dispersan en los sub-repositorios, sino que se unifican en el directorio raíz del repositorio principal openspec/, los sub-repositorios por lo tanto se mantienen puros.

Dos niveles de significado, no los confundas

En la guía oficial de MonoSpecs, se señala explícitamente un lugar muy fácil de confundir: MonoSpecs en realidad tiene dos niveles de significado.

Un nivel es la capa del sistema de configuración, que se refiere al archivo de configuración .hagicode/monospecs.yaml en sí mismo, junto con sus mecanismos de carga, validación y almacenamiento en caché.

Otro nivel es la capa de tipo de repositorio, que se refiere a un modo de organización de repositorios “repositorio principal + múltiples sub-repositorios + specs centralizados”. Cuando decimos que un proyecto “es un proyecto MonoSpecs”, significa que adopta esta estructura.

Estos dos niveles superpuestos juntos forman MonoSpecs completo. Muchas personas que lo接触 por primera vez tienden a ver solo la capa del archivo YAML, pensando que MonoSpecs es solo una lista de configuración, en realidad su valor está más en la segunda capa: un paradigma claro de colaboración de múltiples repositorios. De hecho, las cosas bellas a menudo no están en la primera mirada, hay que mirar más veces.

Por qué se dice que es “actualización y extensión”

Poniendo ambos juntos para comparar, la relación se aclara:

DimensiónOpenSpecMonoSpecs
EnfoqueContenido y ciclo de vida de archivos specEstructura organizacional y lista de repositorios
Producto centralopenspec/specs/*/spec.md.hagicode/monospecs.yaml
¿Depende del otro?No depende de MonoSpecsDepende de OpenSpec, reutiliza su openspec/ para gestión de cambios
Puntos de dolor resueltosCómo escribir y evolucionar especificacionesCómo declarar múltiples repositorios, cómo clonar, cómo entender IA
Alcance de acciónCualquier repositorio puede usarloDiseñado específicamente para estructura de múltiples repositorios “un principal múltiples hijos”

Para ser honestos, MonoSpecs no reemplaza a OpenSpec, sino que agrega una capa de “gobernanza de repositorios” encima de él. Usa monospecs.yaml para describir la topología de repositorios, usa openspec/ centralizado para desacoplar specs de sub-repositorios, usa commit_when_archive para que el archivado se guarde automáticamente en el repositorio principal.

Si usamos una analogía: OpenSpec proporciona la “sintaxis de cambios”, MonoSpecs proporciona la “semántica de múltiples repositorios”. El primero es el requisito previo del segundo, el segundo es la extensión del primero. Todos los caminos llevan a Roma, solo que esta vez, el camino es un poco más largo de lo imaginado.

Cómo implementar: cuatro pasos

Primer paso: establecer el repositorio principal y el archivo de configuración

Pon el archivo de configuración en el directorio raíz del monorepo, declarando todos los sub-repositorios. Con nuestro propio proyecto como ejemplo, la estructura es aproximadamente esta:

.hagicode/monospecs.yaml
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: "Documentación"
tags: [docs, astro, starlight]
ui:
collapseToMore: true # Colapsar en "More" en la UI

Hay varios campos a los que hay que prestar atención especial:

  • path es la ruta local relativa a la raíz del repositorio principal, también es la clave única de cada registro.
  • url es la dirección remota de Git, el script de clonación depende de ella para obtener código.
  • displayName / icon / tags solo afectan la visualización de UI y el contexto de IA, no afectan el comportamiento de clonación.
  • commit_when_archive: true hace que las propuestas de OpenSpec se guarden automáticamente en el repositorio principal al archivarse.

Segundo paso: subir OpenSpec al directorio raíz del repositorio principal

Comparación antes y después de la migración:

Antes de migración (specs atrapados en sub-repositorio) Después de migración (specs centralizados en repositorio principal)
hagicode-core/ . (raíz del repositorio principal)
└── openspec/ ├── .hagicode/monospecs.yaml
└── specs/ (82+ specs) ├── openspec/
│ ├── specs/ (gestión centralizada)
│ └── changes/
└── repos/
├── hagicode-core/ (puro, sin openspec)
├── web/
└── docs/

De ahora en adelante, los sub-repositorios ya no cargan openspec/, el repositorio principal se convierte en la única fuente de verdad de specs. Este paso parece simple, pero los beneficios son muy reales: cualquier ingeniero de pie en el directorio raíz del repositorio principal puede ver todas las especificaciones de todo el sistema de productos.

Tercer paso: hacer que el script de clonación lea configuración en lugar de codificar

La lógica central de scripts/clone-repos.mjs es leer YAML, clonar uno por uno:

const CONFIG_PATH = path.join(__dirname, '..', '.hagicode', 'monospecs.yaml');
// Analizar el array repositories
// Ejecutar git clone <url> <path> para cada elemento
// Si el directorio de destino ya existe, saltar o git pull

Al agregar un nuevo repositorio, solo necesitas agregar una línea en el YAML, sin tocar el script. Este pequeño cambio ahorra innumerables discusiones de “olvidé sincronizar la lista de repositorios”. Después de todo, ¿quién quiere trabajo repetitivo?

Cuarto paso: el backend proporciona una capa de servicio MonoSpecs unificada

Si no extraes una capa de abstracción, la lógica de análisis de configuración se dispersa fácilmente en varios rincones como GitAppService, ProjectAppService. HagiCode extrajo IMonoSpecsService en el módulo ClaudeHelper, exponiendo un 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 servicio es responsable de cargar, validar y almacenar en caché la configuración, y proporciona la capacidad de “inicializar plantilla mínima”: generar monospecs.yaml, repos/, openspec/changes/archive/, openspec/specs/ esqueleto con un clic para un proyecto vacío, y automáticamente completar .gitignore. El almacenamiento en caché es como la memoria, una vez recordado, la próxima vez no hay que esforzarse en pensar.

Varios problemas en la práctica

Inicializar un proyecto MonoSpecs completamente nuevo

Después de llamar a InitializeManagementDocumentAsync, aparecerá esta estructura en el disco:

my-project/
├── .gitignore # Agregar regla de ignorar repos/ (idempotente, no agregar duplicado)
├── .hagicode/
│ └── monospecs.yaml # Plantilla mínima: version / commit_when_archive / repositories: []
├── openspec/
│ ├── changes/archive/
│ └── specs/
└── repos/ # Directorio vacío, esperando clonación

Hay varios bordes a tener en cuenta, todos extraídos de la especificación:

  • Idempotente: los directorios repos/, openspec/ existentes se conservarán, no darán error.
  • No sobrescribir: si monospecs.yaml ya existe y se puede analizar normalmente, la inicialización no lo tocará, solo completará las reglas .gitignore faltantes y el directorio openspec.
  • Rechazar configuración sucia: monospecs.yaml existente pero no analizable será rechazado directamente, devolviendo información de error diagnosticable, absolutamente no se sobrescribirá.
  • No escanear automáticamente: la inicialización no tomará la iniciativa de escanear directorios del disco en entradas de repositorio, repositories está vacío por defecto, necesitas completarlo manualmente o a través de UI.

Trampa de migración de ubicación del archivo de configuración

Históricamente monospecs.yaml se colocó en el directorio raíz del proyecto, luego se migró forzosamente a .hagicode/monospecs.yaml. Este punto está muy claro en la especificación:

El monospecs.yaml en el directorio raíz ya no se detecta, ni como reserva de compatibilidad. El script de clonación solo reconoce .hagicode/monospecs.yaml.

Por lo tanto, al actualizar proyectos antiguos, debes ejecutar manualmente mv monospecs.yaml .hagicode/monospecs.yaml, sin ningún camino de compatibilidad silenciosa. A primera vista parece un poco rígido, pero piénsalo bien, esto es para eliminar completamente la ambigüedad de “ambas ubicaciones pueden tener efecto”: una vez que existe esta ambigüedad, al solucionar problemas puede volver loca a la gente, después de todo, nadie quiere buscar respuestas entre dos archivos.

Validación al guardar: no escribas configuración inválida

Antes de escribir de vuelta a través de SaveManagementDocumentAsync, el servicio hará validación a nivel de campo. Varios escenarios típicos de rechazo:

  • Dos entradas de repositorio con path duplicado → rechazar, devolver campo en conflicto.
  • Cualquier entrada falta path → rechazar, devolver error de campo requerido.
  • url no está vacío pero no es una URL absoluta válida → rechazar.

Solo después de pasar la validación se serializa como YAML y se escribe en disco, al mismo tiempo se invalida el caché de configuración de la ruta del proyecto, garantizando que la siguiente lectura obtenga el contenido más reciente. Este paso parece trivial, pero puede evitar innumerables tickets de “por qué no funciona mi cambio de configuración”, después de todo, si hay muchos tickets como este, nadie puede aguantar.

Modo workspace vs modo manual de repositories

El archivo de configuración admite dos formas de derivar la lista de repositorios.

Una es el modo manual de repositories, que lista cada repositorio directamente en YAML, el documento de administración se marca como editable.

Otra es el modo workspace, que declara un archivo .code-workspace, del cual se deriva la lista de repositorios. En este modo el documento de administración se marca como solo lectura, prohibiendo reescribir directamente el array de repositorios, solo se pueden cambiar campos de nivel superior admitidos.

Nuestro propio HagiCode Mono actualmente ha comentado el modo workspace, adoptando el modo manual. La razón es simple: el modo manual puede controlar con precisión el icon y tags de cada repositorio, el efecto de visualización de UI es más controlable. ¿Cómo decirlo? Las cosas que puedes controlar siempre te hacen sentir más tranquilo.

Sugerencias prácticas para Agentes de IA

Ahora que la programación con IA es cada vez más popular, el esquema MonoSpecs en realidad tiene un valor implícito: proporciona un mapa estructurado del proyecto para la IA.

En la colaboración de múltiples repositorios, AGENTS.md y monospecs.yaml son dos contextos clave para la IA. El flujo de trabajo sugerido es este:

  1. Primero lee monospecs.yaml para obtener la topología de repositorios, aclara quién es editable, quién es solo referencia.
  2. Luego lee el “Active Edit Scope” de AGENTS.md raíz, confirma el alcance permitido para modificaciones actuales.
  3. Los cambios entre repositorios se escriben como propuestas en openspec/changes/ del repositorio principal raíz, no inicies openspec separado en cada sub-repositorio.

Este convenio permite que la IA entienda establemente la división de “el repositorio principal gestiona specs, los sub-repositorios gestionan código”, y no escribirá erróneamente spec en sub-repositorios: esta operación incorrecta hemos pisado el pozo varias veces. De hecho, no culpar a la IA, después de todo los sub-repositorios y el repositorio principal se ven muy similares, ¿quién puede distinguirlos de un vistazo?

Resumen

Resumen en una frase: OpenSpec define “cómo escribir cambios”, MonoSpecs define “cómo organizar repositorios”.

El primero es la base gramatical del segundo, el segundo extiende el primero del contexto de repositorio único al contexto de múltiples repositorios, y con una lista YAML converge la topología de repositorios, el proceso de clonación, el contexto de IA y la pertenencia de specs de una vez. Este es el verdadero significado de “monospec es una actualización y extensión de openspec”: no es reemplazo, sino agregar una capa de semántica de múltiples repositorios encima de él.

Si también estás haciendo productos de múltiples repositorios de escala similar, considera si estos dos niveles están bien establecidos. Por más bonitas que estén escritas las especificaciones, sin una gobernanza de repositorios clara que las soporte, al final se convertirá en un desastre…

Referencias

Resumen

En torno a “MonoSpecs ¿Qué es y por qué se dice que es una actualización y extensión de OpenSpec”, una forma más sólida de avanzar es primero hacer funcionar gradualmente la configuración clave, los límites de dependencia y la ruta de implementación, luego completar los detalles de optimización.

Cuando los objetivos, pasos y puntos de aceptación estén claros,这类 esquema generalmente puede entrar en entrega real de manera más fluida.

开始使用 HagiCode

一次安装,几分钟上手

HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。