Was ist MonoSpecs: Warum es eine Weiterentwicklung und Erweiterung von OpenSpec ist
Was ist MonoSpecs: Warum es eine Weiterentwicklung und Erweiterung von OpenSpec ist
Wenn ein Produktsystem auf 40+ unabhängige Git-Repositories anwächst, wo sollten die “Spezifikationen” untergebracht werden? Dieser Beitrag behandelt die zwei Schritte, die HagiCode bei der Multi-Repository-Governance unternommen hat: Zunächst wurde OpenSpec in das Hauptrepository hochgezogen, und darauf aufbauend wurde MonoSpecs als Multi-Repository-Lösung entwickelt. Eigentlich ist es nichts Besonderes, wir haben nur einige Hürden überwunden und wollten sie dokumentieren.
Hintergrund
Wer schon einmal etwas größere Produkte entwickelt hat, kennt wahrscheinlich diese Erfahrung – am Anfang gibt es nur ein Repository, ordentlich und ruhig; später werden Frontend, Backend, Desktop, Dokumentationsseite, Website, Build-Tools jeweils zu unabhängigen Repositories, und die Anzahl der Repositories schießt in die Höhe wie Unkraut, das man nicht mehr stoppen kann. Später möchten Sie eine “Spezifikation” für eine funktionsübergreifende Funktion schreiben und wissen plötzlich nicht mehr, wohin damit – etwas wie das Taschengeld in der Kindheit, das plötzlich verschwunden ist.
Unser eigenes HagiCode ist genau ein solches Produktsystem aus 40+ unabhängigen Git-Repositories. In der Anfangszeit haben wir das openspec/-Verzeichnis von OpenSpec direkt in das Backend-Subrepository hagicode-core gesteckt, in der Annahme, dass das Backend ohnehin der Kern ist und es dort am sichersten ist. Als die Repositories jedoch immer weiter aufgeteilt wurden, offenbarte dieses Lösungsansatz eine Reihe von Problemen, die einem Kopfschmerzen bereiten. Schließlich ist die Code-Welt niemals stabil, nur weil man es “stabil” gedacht hat.
Der erste Schmerzpunkt: Specs sind in einem einzelnen Subrepository gefangen. Wenn eine Funktion sowohl das Frontend web als auch das Backend hagicode-core betrifft, muss ich den Vorschlag in hagicode-core schreiben und dann in die anderen Subrepositories gehen, um die Codeänderungen durchzuführen. Welchem Repository der Vorschlag zugeordnet werden soll, wird selbst zum Streitpunkt.
Der zweite Schmerzpunkt: Subrepositories sind nicht rein. Jedes Subrepository trägt sein eigenes openspec/, Spezifikationsdokumente und Produktcode sind vermischt. Jemand clont Ihr Frontend-Repository und bringt dabei eine Menge Backend-Vorschlagsdokumente mit, völlig verwirrt.
Der dritte Schmerzpunkt: AI Agents haben Schwierigkeiten, die Repository-Beziehungen zu verstehen. Die einzelnen Subrepositories sind voneinander unabhängig, und es gibt keine maschinenlesbare “Liste”, die der AI mitteilt: Aus welchen Repositories besteht dieses Produkt? Was ist wofür verantwortlich? Welches ist editierbar? Welches ist nur als Referenz schreibgeschützt?
Der vierte Schmerzpunkt: Hohe Kosten für übergreifendes Editieren. Um eine spec zu ändern, muss man erst cd in das entsprechende Submodul, Pfade springen hin und her, die mentale Belastung für die Zusammenarbeit ist enorm.
Vor diesem Hintergrund haben wir zunächst eine “OpenSpec Monorepo Migration” durchgeführt und die specs von den Subrepositories in das Stammverzeichnis des monorepo hochgezogen. Darauf aufbauend haben wir MonoSpecs als Multi-Repository-Lösung entwickelt. Das Verständnis dieser progressiven Beziehung ist der Schlüssel zum Verständnis von “warum MonoSpecs eine Weiterentwicklung und Erweiterung von OpenSpec ist”.
Über HagiCode
Die in diesem Beitrag vorgestellte Lösung stammt aus unserer praktischen Erfahrung im HagiCode-Projekt. HagiCode ist ein AI-Code-Assistent-Projekt mit vielen Repositories und häufiger sprachübergreifender Zusammenarbeit. Diese strukturelle Komplexität zwingt uns dazu, sowohl “Spezifikationen” als auch “Repository-Governance” solide zu gestalten. Die MonoSpecs-Lösung wurde in der Multi-Repository-Praxis schrittweise optimiert – es gibt keine genialen Ideen, wir haben nur ein paar Schritte mehr unternommen.
OpenSpec löst “Wie schreibt man Specs und wie entwickeln sie sich weiter”
Um die Beziehung zwischen den beiden klarzustellen, müssen wir zunächst untersuchen, wofür sie jeweils zuständig sind.
OpenSpec ist im Wesentlichen ein spec-driven Änderungsmanagement-Workflow. Seine Kernprodukte sehen so aus:
openspec/├── specs/ # Aktuelle Fähigkeitsspezifikationen (eine spec.md pro Fähigkeit)├── changes/ # Laufende Vorschläge│ └── archive/ # Archivierte historische Vorschläge└── project.mdEs beantwortet die Frage: Eine Änderung durchläuft einen Lebenszyklus aus Vorschlag (proposal), Design (design), Aufgaben (tasks), Archivierung (archive) und fügt bei der Archivierung die Deltas in die specs ein. Dieser Mechanismus selbst ist unabhängig von “wie viele Repositories es gibt, wo sie sind und wer sie verwaltet” – er kümmert sich nur darum, wie spec-Dateien organisiert sind.
Durch einen Migrationsvorschlag haben wir die ursprünglich in hagicode-core/openspec/ verteilten 82+ spec-Dateien in das openspec/ im Stammverzeichnis des monorepo hochgezogen, damit alle specs an einem Ort einheitlich sichtbar und versioniert sind.
Aber diese Migration war im Grunde genommen nur “das Umziehen von spec-Dateien” und beantwortete nicht die grundlegendere Frage: Aus welchen Subrepositories besteht dieses monorepo eigentlich? Was sind die Beziehungen zwischen diesen Subrepositories? Das ist der Teil, den MonoSpecs ergänzt.
MonoSpecs löst “Wie verwaltet man Multi-Repositories selbst”
Der Kern von MonoSpecs ist eine maschinenlesbare Manifestdatei: .hagicode/monospecs.yaml. Sie macht vier Dinge, die OpenSpec überhaupt nicht betrifft.
Erstens: Deklaration des Subrepository-Manifests. Pfad, URL, displayName, icon, tags, ob in “More” eingeklappt wird – alles wird in einem YAML geschrieben, auf einen Blick.
Zweitens: Treiber für Clone-Skripte. scripts/clone-repos.mjs liest dieses YAML direkt und führt massenhaft git clone aus, ohne die Repository-Liste zu hartcodieren. Neue Repositories erfordern nur eine Zeile im YAML, das Skript braucht null Änderungen.
Drittens: Projektstruktur-Kontext für AI/IDE bereitstellen. In Verbindung mit AGENTS.md kann der AI Agent sofort erkennen, welches Repository editierbar ist, welches nur als Referenz dient und welcher Tech-Stack verwendet wird.
Viertens: OpenSpec-Produkte im Hauptrepository verankern. Specs werden nicht mehr auf die einzelnen Subrepositories verteilt, sondern zentral im openspec/ im Stammverzeichnis des Hauptrepositories zusammengeführt. Subrepositories bleiben dadurch sauber.
Zwei Bedeutungsebenen, verwechseln Sie sie nicht
Im offiziellen MonoSpecs-Guide wird ein sehr häufig verwechselbarer Punkt deutlich gemacht: MonoSpecs hat tatsächlich zwei Bedeutungsebenen.
Eine Ebene ist die Konfigurationssystemebene, die sich auf die Konfigurationsdatei .hagicode/monospecs.yaml selbst sowie die zugehörigen Lademechanismen, Validierung und Caching bezieht.
Die andere Ebene ist die Repository-Typenebene, die sich auf ein Repository-Organisationsmodell “Hauptrepository + mehrere Subrepositories + zentrale specs” bezieht. Wenn wir sagen, ein Projekt “ist ein MonoSpecs-Projekt”, meinen wir, dass es diese Struktur übernimmt.
Diese beiden Ebenen zusammen ergeben das vollständige MonoSpecs. Viele Menschen sehen beim ersten Kontakt nur die YAML-Dateiebene und denken, MonoSpecs sei nur eine Konfigurationsliste, aber sein Wert liegt eher in der zweiten Ebene – ein klares Paradigma für die Multi-Repository-Zusammenarbeit. Schöne Dinge sind oft nicht auf den ersten Blick zu erkennen, man muss sie mehrmals betrachten.
Warum ist es eine “Weiterentwicklung und Erweiterung”
Wenn man beide nebeneinander vergleicht, wird die Beziehung klar:
| Dimension | OpenSpec | MonoSpecs |
|---|---|---|
| Fokus | Inhalt und Lebenszyklus von spec-Dateien | Organisationsstruktur und Liste von Repositories |
| Kernprodukt | openspec/specs/*/spec.md | .hagicode/monospecs.yaml |
| Abhängigkeit | Nicht von MonoSpecs abhängig | Abhängig von OpenSpec, nutzt openspec/ für Änderungsmanagement |
| Gelöste Schmerzpunkte | Wie schreibt man Specs und wie entwickeln sie sich weiter | Wie deklariert man Multi-Repositories, wie clont man, wie versteht AI |
| Geltungsbereich | In jedem Repository verwendbar | Speziell für “ein Haupt, mehrere Sub”-Multi-Repository-Strukturen entwickelt |
Kurz gesagt, ersetzt MonoSpecs nicht OpenSpec, sondern fügt eine Ebene der “Repository-Governance” darüber hinzu. Mit monospecs.yaml wird die Repository-Topologie beschrieben, mit zentralem openspec/ werden specs von Subrepositories entkoppelt, und mit commit_when_archive werden Archivierungen automatisch im Hauptrepository gespeichert.
Wenn man eine Analogie verwendet: OpenSpec stellt die “Änderungssyntax” bereit, MonoSpecs stellt die “Multi-Repository-Semantik” bereit. Ersteres ist die Voraussetzung für Letzteres, Letzteres ist die Erweiterung von Ersterem. Alle Wege führen nach Rom, diesmal war der Weg aber etwas länger als erwartet.
Wie man es umsetzt: Vier Schritte
Schritt 1: Hauptrepository und Konfigurationsdatei festlegen
Platzieren Sie die Konfigurationsdatei im Stammverzeichnis des monorepo und deklarieren Sie alle Subrepositories. Als Beispiel für unser eigenes Projekt sieht die Struktur ungefähr so aus:
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: "Dokumentation" tags: [docs, astro, starlight] ui: collapseToMore: true # In UI hinter "More" einklappenEinige Felder verdienen besondere Aufmerksamkeit:
pathist der lokale Pfad relativ zum Stammverzeichnis des Hauptrepositories und auch der eindeutige Schlüssel für jeden Eintrag.urlist die Git-Remote-Adresse, auf die das Clone-Skript angewiesen ist, um Code zu ziehen.displayName/icon/tagsbeeinflussen nur die UI-Darstellung und den AI-Kontext, nicht das Clone-Verhalten.commit_when_archive: truesorgt dafür, dass OpenSpec-Vorschläge bei der Archivierung automatisch im Hauptrepository committet werden.
Schritt 2: OpenSpec in das Stammverzeichnis des Hauptrepositories hochziehen
Vergleich vor und nach der Migration:
Vor der Migration (specs in Subrepository gefangen) Nach der Migration (specs zentral im Hauptrepository)hagicode-core/ . (Stammverzeichnis des Hauptrepositories)└── openspec/ ├── .hagicode/monospecs.yaml └── specs/ (82+ specs) ├── openspec/ │ ├── specs/ (zentrale Verwaltung) │ └── changes/ └── repos/ ├── hagicode-core/ (sauber, ohne openspec) ├── web/ └── docs/Subrepositories tragen von nun an kein openspec/ mehr, das Hauptrepository wird zur einzigen Quelle der Wahrheit für specs. Dieser Schritt sieht einfach aus, bringt aber sehr realen Nutzen – jeder Ingenieur, der im Stammverzeichnis des Hauptrepositories steht, kann alle Spezifikationen des gesamten Produktsystems sehen.
Schritt 3: Clone-Skript Konfiguration statt Hartcodierung lesen lassen
Die Kernlogik von scripts/clone-repos.mjs besteht darin, das YAML zu lesen und jedes Repository zu clonen:
const CONFIG_PATH = path.join(__dirname, '..', '.hagicode', 'monospecs.yaml');// repositories-Array parsen// Für jeden Eintrag git clone <url> <path> ausführen// Wenn Zielverzeichnis existiert, überspringen oder git pullWenn ein neues Repository hinzugefügt wird, muss nur eine Zeile im YAML hinzugefügt werden, das Skript muss nicht geändert werden. Diese kleine Änderung spart unzählige Diskussionen über “Vergessen, die Repository-Liste zu synchronisieren”. Schließlich möchte niemand wiederholte Arbeit leisten.
Schritt 4: Backend bietet eine einheitliche MonoSpecs-Serviceebene
Wenn man keine Abstraktionsschicht extrahiert, kann die Konfigurations-Parsing-Logik leicht über GitAppService, ProjectAppService und andere Ecken verstreut sein. HagiCode hat IMonoSpecsService im ClaudeHelper-Modul extrahiert und macht eine Reihe klarer Fähigkeiten nach außen zugänglich:
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);}Dieser Service ist verantwortlich für das Laden, Validieren und Caching der Konfiguration und bietet die Möglichkeit, “minimale Vorlagen zu initialisieren” – für ein leeres Projekt ein monospecs.yaml, repos/, openspec/changes/archive/, openspec/specs/ Skelett mit einem Klick zu generieren und automatisch .gitignore zu ergänzen. Caching ist wie Erinnerung – wenn man es sich merkt, muss man beim nächsten Mal nicht mehr so anstrengen.
Einige Hürden in der Praxis
Ein neues MonoSpecs-Projekt initialisieren
Nach dem Aufrufen von InitializeManagementDocumentAsync erscheint auf der Festplatte folgende Struktur:
my-project/├── .gitignore # Neue repos/-Ignorierregeln (idempotent, keine doppelten Anhänge)├── .hagicode/│ └── monospecs.yaml # Minimale Vorlage: version / commit_when_archive / repositories: []├── openspec/│ ├── changes/archive/│ └── specs/└── repos/ # Leeres Verzeichnis, wartet auf CloneHier gibt es einige Grenzen zu beachten, alle aus der spec extrahiert:
- Idempotent: Bereits existierende
repos/-,openspec/-Verzeichnisse werden beibehalten, kein Fehler. - Nicht überschreiben: Wenn
monospecs.yamlbereits existiert und normal geparst werden kann, berührt die Initialisierung es nicht, sie fügt nur fehlende.gitignore-Regeln und openspec-Verzeichnisse nach. - Schmutzige Konfiguration ablehnen: Bereits existierende aber nicht parsbare
monospecs.yamlwird direkt abgelehnt, mit diagnostischer Fehlermeldung zurückgegeben, absolut nicht überschrieben. - Kein automatisches Scannen: Die Initialisierung scannt nicht eigenmächtig Festplattenverzeichnisse zu Repository-Einträgen,
repositoriesist standardmäßig leer, muss manuell oder über die UI ausgefüllt werden.
Migrationsfalle für den Speicherort der Konfigurationsdatei
Historisch gesehen war monospecs.yaml im Stammverzeichnis des Projekts, später wurde es erzwungen nach .hagicode/monospecs.yaml migriert. Dies ist in der spec sehr klar formuliert:
monospecs.yamlim Stammverzeichnis wird nicht mehr erkannt und auch nicht als Kompatibilitäts-Rückfall verwendet. Clone-Skripte erkennen nur.hagicode/monospecs.yaml.
Deshalb müssen bei alten Projekten bei einem Upgrade manuell mv monospecs.yaml .hagicode/monospecs.yaml ausgeführt werden, es gibt keinen Pfad der stillen Kompatibilität. Auf den ersten Blick wirkt es etwas unpersönlich, aber wenn man genauer nachdenkt, ist dies, um die Mehrdeutigkeit von “beide Positionen könnten生效” vollständig zu beseitigen – wenn diese Mehrdeutigkeit existiert, kann sie bei der Fehlersuche einen in den Wahnsinn treiben, schließlich möchte niemand zwischen zwei Dateien hin und her nach Antworten suchen.
Speichern-Validierung: Schreiben Sie keine ungültige Konfiguration
Bevor über SaveManagementDocumentAsync zurückgeschrieben wird, führt der Service eine Validierung auf Feldebene durch. Einige typische Ablehnungsszenarien:
- Zwei Repository-Einträge mit gleichem
path→ Ablehnung, Rückgabe des Konfliktfelds. - Einem Eintrag fehlt
path→ Ablehnung, Rückgabe des Pflichtfeldfehlers. urlnicht leer aber keine gültige absolute URL → Ablehnung.
Erst nach erfolgreicher Validierung wird es als YAML auf die Festplatte serialisiert, gleichzeitig wird der Konfigurations-Cache für diesen Projektpfad ungültig gemacht, um sicherzustellen, dass beim nächsten Lesen der neueste Inhalt abgerufen wird. Dieser Schritt wirkt trivial, kann aber unzählige Tickets vermeiden von “warum hat meine Konfigurationsänderung nicht gewirkt” – schließlich kann niemand diese Tickets auf Dauer tragen.
workspace-Modus vs. manueller repositories-Modus
Die Konfigurationsdatei unterstützt zwei Arten, die Repository-Liste abzuleiten.
Eine ist der manuelle repositories-Modus, bei dem jedes Repository direkt im YAML aufgelistet wird, das Verwaltungsdokument ist als editierbar markiert.
Die andere ist der workspace-Modus, bei dem eine .code-workspace-Datei deklariert wird, von der die Repository-Liste abgeleitet wird. In diesem Modus ist das Verwaltungsdokument als schreibgeschützt markiert, direktes Umschreiben des Repository-Arrays ist verboten, es können nur unterstützte Top-Level-Felder geändert werden.
Unser eigenes HagiCode Mono hat derzeit den workspace-Modus auskommentiert und verwendet den manuellen Modus. Der Grund ist einfach: Der manuelle Modus ermöglicht eine präzise Kontrolle über icon und tags jedes Repositorys, die UI-Darstellung ist kontrollierbarer. Man könnte sagen, Dinge, die man kontrollieren kann, geben einem mehr Sicherheit.
Praktische Empfehlungen für AI Agents
Mit der zunehmenden Verbreitung von AI-Programmierung hat die MonoSpecs-Lösung noch einen impliziten Wert: Sie bietet AI eine strukturierte Projektlandkarte.
Bei der Multi-Repository-Zusammenarbeit sind AGENTS.md und monospecs.yaml zwei Schlüsselkontexte für AI. Der empfohlene Workflow ist:
- Zuerst
monospecs.yamllesen, um die Repository-Topologie zu erhalten, klar zu machen, wer editierbar ist und wer nur als Referenz dient. - Dann “Active Edit Scope” im Stamm-
AGENTS.mdlesen, um den aktuell zulässigen Änderungsbereich zu bestätigen. - Für übergreifende Änderungen Vorschläge einheitlich im
openspec/changes/im Stammverzeichnis des Hauptrepositories schreiben, nicht in einzelnen Subrepositories eigene openspec starten.
Diese Konvention ermöglicht AI, die Aufteilung “Hauptrepository verwaltet specs, Subrepositories verwalten Code” stabil zu verstehen und specs nicht versehentlich in Subrepositories zu schreiben – wir haben diesen Fehler schon mehrmals begangen. Eigentlich ist es nicht die Schuld von AI, schließlich sehen Subrepositories und Hauptrepositories so ähnlich aus, wer kann sie auf einen Blick unterscheiden?
Zusammenfassung
Zusammenfassend in einem Satz: OpenSpec definiert “wie Änderungen geschrieben werden”, MonoSpecs definiert “wie Repositories angeordnet sind”.
Ersteres ist die syntaktische Grundlage für Letzteres, Letzteres erweitert Ersteres vom Single-Repository-Kontext zum Multi-Repository-Kontext und konvergiert Repository-Topologie, Clone-Workflow, AI-Kontext und specs-Zugehörigkeit mit einem YAML-Manifest. Das ist die wahre Bedeutung von “MonoSpecs ist eine Weiterentwicklung und Erweiterung von OpenSpec” – kein Ersatz, sondern eine Ebene der Multi-Repository-Semantik darüber.
Wenn Sie auch Multi-Repository-Produkte ähnlicher Größe entwickeln, überlegen Sie, ob diese beiden Ebenen ebenfalls etabliert sind. Spezifikationen können noch so schön geschrieben sein, ohne klare Repository-Governance werden sie schließlich zu einem chaotischen Brei…
Referenzen
- HagiCode-Website
- HagiCode-org/site GitHub-Repository
- OpenSpec-Workflow-Dokumentation
- MonoSpecs-bezogene specs:
monospecs-guide,monospecs-repository-config,monospec-config-management
Zusammenfassung
Um “Was ist MonoSpecs: Warum es eine Weiterentwicklung und Erweiterung von OpenSpec ist” robust voranzutreiben, ist eine sicherere Vorgehensweise, zuerst die Schlüsselkonfiguration, Abhängigkeitsgrenzen und Implementierungspfade schrittweise zu testen und dann die Optimierungsdetails zu ergänzen.
Wenn Ziele, Schritte und Akzeptanzpunkte klar definiert sind, können solche Lösungen in der Regel reibungsloser in die tatsächliche Lieferung übergehen.
开始使用 HagiCode
一次安装,几分钟上手
HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。