OpenSpec UI: ein Live-Dashboard für deine Specs
OpenSpec UI: ein Live-Dashboard für deine Specs
Wer mit einem KI-Agenten und OpenSpec arbeitet, kennt das Bild. Im Projekt wächst ein Verzeichnis openspec/ heran: Changes mit proposal.md, design.md, tasks.md und ihren Delta-Specs, daneben die kanonischen Specs der einzelnen Capabilities. Die Absicht hinter dem Code steht damit endlich geschrieben, statt sich in ihm zu verstecken. Nur verteilt sie sich über Dutzende Markdown-Dateien, und der Editor zeigt eben Dateien. Was er nicht zeigt, ist der Zustand. Woran arbeitet der Agent gerade? Welcher Task ist der nächste offene? Und was hat sich verändert, während ich zehn Minuten woanders hingeschaut habe?
Genau diese Lücke füllt OpenSpec UI, ein kleines Phoenix-LiveView-Dashboard, das lokal neben deinem Projekt läuft und den OpenSpec-Workspace im Browser zeigt — live, während gearbeitet wird. Der Code liegt offen auf GitLab:
https://gitlab.com/public_elixir/openspec_ui
Ein Blick statt Datei-Hopping
Du startest den Server, gibst den absoluten Pfad eines Projekts an, das ein openspec/-Verzeichnis enthält, und bekommst eine Übersicht. Jeder Change ist eine Karte: welches Schema er benutzt, welche Artefakte schon fertig sind, wie weit die Tasks gediehen sind und welche Aufgabe gerade die nächste offene ist. Darunter stehen die Specs mit ihren Requirements, und wer will, klappt die Scenarios auf. Alles, was openspec list, openspec show und openspec status an Wissen herausgeben, liegt an einer Stelle nebeneinander, statt in vier Terminal-Fenstern und sechs Editor-Tabs.
Das klingt banal, ändert aber die Arbeitshaltung. Man liest nicht mehr eine Datei nach der anderen und baut sich im Kopf ein Bild zusammen, sondern sieht das Bild.
Der Agent schreibt, die Seite folgt
Der eigentliche Reiz liegt im Wörtchen „live“. Die Anwendung beobachtet das Verzeichnis <workspace>/openspec rekursiv im Dateisystem. Weil Werkzeuge beim Anlegen eines Changes mehrere Dateien in schneller Folge schreiben, werden die Events erst gebündelt und eine halbe Sekunde später ausgewertet; dann wird der State neu ermittelt und an alle geöffneten Ansichten verteilt.
Der Effekt im Alltag: Du lässt den Agenten im Terminal einen Change anlegen und siehst im zweiten Monitor, wie die Karte erscheint. Er hakt eine Aufgabe ab, der Fortschrittsbalken rückt vor. Er ergänzt ein Requirement, und die geöffnete Spec zeigt es. Kein Reload, kein „F5 aus Gewohnheit“.
Damit das bezahlbar bleibt, wird nicht bei jedem Wimpernschlag alles neu gelesen. Unveränderte Changes und Specs behalten ihr vorheriges Ergebnis; neu eingelesen wird nur, was die Dateisystem-Events tatsächlich berührt haben.
„Was hat sich seit meinem letzten Blick geändert?“
Diese Frage stellt sich immer dann, wenn man vom Kaffee oder aus einem Meeting zurückkommt. Die Anwendung beantwortet sie, indem sie zwei aufeinanderfolgende Zustände vergleicht und die Unterschiede als Verlauf führt: neuer Change, Change verschwunden, Aufgabe abgehakt, Artefakt fertiggestellt, Requirement ergänzt, Spec bearbeitet. Das Jüngste steht oben, die betroffenen Karten sind markiert, und mit einem Klick tut man alles ab und fängt einen neuen Beobachtungszeitraum an.
Das ist der Unterschied zwischen „irgendwas ist passiert“ und „diese drei Dinge sind passiert“.
Reviewen im Browser
Ein Change hat eine eigene Route. Dort liegen seine textuellen Artefakte als gerendertes Markdown in Reitern nebeneinander, dazu die Delta-Specs mit Verweis auf die Haupt-Spec, die sie verändern. Ein Proposal liest sich so, wie es gemeint ist — als Dokument, nicht als Diff.
Und weil man beim Lesen ohnehin mitdenkt, kann man von hier aus auch die aktuelle Aufgabe abschließen oder die zuletzt erledigte wieder öffnen. Das ist bewusst der einzige Weg, auf dem die Anwendung eine Datei deines Workspaces anfasst.
Was unter der Haube passiert
Drei Entscheidungen prägen das Projekt, und alle drei stehen als Requirement in seinen eigenen Specs.
Die CLI ist die Quelle, nicht das Markdown. Die Anwendung liest die Dateien nicht selbst, um daraus Struktur abzuleiten, sondern fragt die openspec-CLI im JSON-Modus. Das Dateiformat gehört OpenSpec; eine Oberfläche, die es nachbaut, wäre beim nächsten Release kaputt.
Lesen ist die Regel, Schreiben die begründete Ausnahme. Die Spec formuliert das unmissverständlich:
### Requirement: Nur lesende CLI-Kommandos
Das System SHALL ausschließlich lesende `openspec`-Kommandos ausführen und
SHALL kein Kommando ausführen, das Dateien im beobachteten Workspace erzeugt,
ändert oder löscht. Das System SHALL Kommandos ohne Shell-Interpretation
ausführen und den Workspace-Pfad nicht als Teil einer Kommandozeichenkette
interpolieren.
#### Scenario: State-Ermittlung schreibt nicht
- **WHEN** das System State für einen Workspace ermittelt
- **THEN** ändert, erzeugt und löscht es dabei keine Datei des Workspaces
Die einzige Ausnahme ist die Checkbox in der tasks.md, denn dafür kennt die CLI kein Kommando. Auch dieser eine Schreibvorgang ist eingezäunt: Welche Zeile gemeint ist, sagt die Kennung der CLI, der Text dieser Zeile muss es bestätigen, und geändert wird nichts außer der Marke selbst. Hat jemand die Datei zwischenzeitlich umgebaut, wird nicht geraten, sondern abgebrochen.
Kein Server, keine Datenbank, keine Migration. Der Zustand liegt im Dateisystem, wo er hingehört. Das Einzige, was die Anwendung selbst speichert, ist die Liste der zuletzt benutzten Workspaces — und die liegt im Konfigurationsverzeichnis des Nutzers, nicht in deinem Projekt. Dahinter steckt gewöhnliches OTP: ein Beobachter je Workspace über Registry und DynamicSupervisor, CLI-Aufrufe in einem Task.Supervisor mit Zeitgrenze, Verteilung über PubSub.
Das Werkzeug baut sich mit dem Verfahren, das es zeigt
Im Repository liegt ein openspec/-Verzeichnis mit acht Capabilities und elf archivierten Changes. Jede Funktion, die dieser Artikel beschreibt, hat als Proposal angefangen, ist dann zur Spec geworden, dann zu einer Task-Liste und erst danach zu Code. Wer die Anwendung auf ihr eigenes Verzeichnis richtet, sieht in ihr ihre eigene Entstehungsgeschichte — als Demo schwer zu schlagen.
Für Mitmacher ist das mehr als eine Spielerei, es ist der Einstieg. Statt sich durch lib/ zu lesen und zu raten, was Absicht und was Zufall war, liest man openspec/specs/<capability>/spec.md und weiß, was gelten soll. Die rund 260 Tests des Projekts sind größtenteils direkt aus den WHEN/THEN-Szenarien dieser Specs entstanden. Wer wissen will, warum etwas so gelöst ist und nicht anders, findet die Abwägung in der design.md des zugehörigen Changes im Archiv.
Die Regel, die den Agenten bei der Stange hält
Diese Reihenfolge hält sich nicht von allein ein. Ein Agent, den man um eine Kleinigkeit bittet, erledigt die Kleinigkeit — sofort, im Code, denn genau dafür ist er da. Nach drei solchen Kleinigkeiten beschreibt die Spec einen Stand, den es nicht mehr gibt. Deshalb steht in der CLAUDE.md des Projekts eine Regel, die dem einen Riegel vorschiebt:
# Enforcement Rule: OpenSpec-First Strategy
## Strict Code Modification Policy
1. NEVER modify production code (e.g. files in `lib/`, `src/`, etc.) without an
active or updated OpenSpec change specification in `openspec/`.
2. BEFORE applying any code change, verify that the change is reflected in the
relevant spec file (EARS requirements, scenarios, or proposal tasks).
3. IF the user asks for a feature, bugfix, or refactoring without specifying a
spec update:
- STOP immediately before writing code.
- Propose the OpenSpec update first.
- Ask for confirmation or automatically apply the spec change before
modifying code.
4. Always follow the OpenSpec workflow (Propose -> Update Spec -> Apply Code -> Verify).
Im Alltag fühlt sich das zunächst wie Sand im Getriebe an. Man wirft ein „bau da noch schnell einen Filter ein“ hin und bekommt keinen Diff zurück, sondern ein Proposal und die Rückfrage, ob das so gemeint war. Genau das ist aber der Sand, den man haben will: Er zwingt einen dazu, das gewünschte Verhalten einmal auszuformulieren, bevor jemand es implementiert. Und die Spec bleibt das, was sie sein soll — die Beschreibung dessen, was gilt, nicht ein Protokoll dessen, was mal galt.
Damit die Regel scharf bleibt, sind ihre Ausnahmen ausdrücklich benannt statt dem Ermessen überlassen: das Übersetzen von Kommentaren und Dokumentation, rein formatierende Änderungen — und Texte wie dieser Artikel, der unter docs/blog/ liegt und niemandes Verhalten spezifiziert. Eine Regel, die im Zweifel verhandelbar ist, gilt nach kurzer Zeit nicht mehr; eine Regel mit drei klar umrissenen Löchern hält.
Der Nebeneffekt schließt den Kreis zum Dashboard: Weil vor jedem Codewechsel ein Change existieren muss, hat die Anwendung überhaupt etwas anzuzeigen. Der Fortschrittsbalken, der sich bewegt, während der Agent arbeitet, ist kein Beiwerk der Disziplin, sondern ihr Abbild.
Warum mitmachen?
Das Projekt ist klein genug, um es an einem Abend zu verstehen: rund 3.800 Zeilen in lib/, gewöhnliches Phoenix 1.8 mit LiveView 1.1 und Tailwind 4, keine Datenbank, keine exotischen Abhängigkeiten. Es gibt nichts, was man erst verstehen müsste, bevor man etwas ändern darf.
Der Beitragsweg ist derselbe Zyklus, den die Anwendung anzeigt: explore, wenn die Idee noch unscharf ist, dann propose, apply und archive. Ein Merge Request bringt den Change-Ordner, den Code und die Tests zusammen mit — und die Diskussion über das gewünschte Verhalten passiert am Proposal, wo sie billig ist, nicht im Diff, wo sie teuer wird.
Offene Baustellen gibt es reichlich, und manche sind interessanter als sie klingen. Ein Beispiel: Das Dashboard zeigt den Archiv-Schritt nicht, weil die CLI in Version 1.6.0 keinen lesenden Weg zu archivierten Changes anbietet. Man könnte die Verzeichnisnamen unter openspec/changes/archive/ selbst lesen, müsste dafür aber genau das Requirement aufweichen, das oben zitiert ist. Oder man bringt das Thema bei OpenSpec ein. Das ist keine Fleißaufgabe, sondern eine Entscheidung — und Entscheidungen trifft man besser zu zweit.
Auch abseits davon ist Platz: mehr Ansichten auf denselben State, bessere Darstellung großer Specs, Filter und Suche, Feinschliff an der Oberfläche. Und wer Elixir gerade erst lernt, findet hier ein überschaubares LiveView-Projekt mit ordentlicher Testabdeckung und einer Spezifikation, die vorher sagt, was herauskommen soll.
Loslegen
Vorausgesetzt sind Elixir und Erlang sowie die openspec-CLI im PATH (siehe openspec.dev). Dann:
git clone https://gitlab.com/public_elixir/openspec_ui.git
cd openspec_ui
mix setup
mix phx.server
Im Browser localhost:4000 öffnen und den absoluten Pfad eines Projekts eintragen, das ein openspec/-Verzeichnis enthält. Zur Not tut es das geklonte Repository selbst.
Fazit
Spec Driven Development verlegt die Wahrheit über ein System aus dem Code in lesbare, versionierte Dokumente. Das ist ein Gewinn — solange man diese Dokumente auch überblickt. OpenSpec UI macht aus dem Stapel Markdown eine Ansicht, die sich von allein aktuell hält, und beantwortet die zwei Fragen, die man beim Arbeiten mit einem Agenten ständig hat: Wo stehen wir gerade, und was hat sich geändert?
Das Projekt ist jung, klein und quelloffen. Wenn dir beim Ausprobieren etwas fehlt, ist der Weg dorthin kurz: Repository klonen, Proposal schreiben, loslegen.
Kommentare