Lernziele
Nach diesem Kapitel kannst du:
- erklären, warum parallele Agenten an geteiltem Zustand scheitern und was „Single Writer“ dagegen tut.
- ein Vorhaben als Abhängigkeitsgraph zerlegen und erkennen, welche Teile wirklich parallel laufen können.
- begründen, warum gemeinsame Verträge vor der parallelen Implementierung stehen müssen.
- Leases, Merge-Reihenfolge und Integrationsprüfung als Bausteine der Zusammenführung einordnen.
- in der Fabrik einen Workflow anlegen, Schreibbereiche und Verträge erklären, die Merge Queue lesen und mit Konflikten, veralteten Tasks und Replanning umgehen.
- Spec-Bindung je Task nutzen, damit alle parallelen Agenten gegen dieselbe Anforderungsfassung arbeiten.
7.1 Warum überhaupt parallel?
Ein einzelner Agent arbeitet sequenziell: lesen, planen, ändern, testen, nachbessern. Bei größeren Vorhaben dauert das, und vieles daran ist voneinander unabhängig. Das Backend-Modul für Rechnungen und das Frontend-Formular für Adressen berühren sich kaum. Warum sollten sie nacheinander entstehen?
Parallelität verspricht drei Dinge:
- Durchlaufzeit: Unabhängige Teile entstehen gleichzeitig.
- Fokus: Jeder Agent bekommt einen kleineren, klar umrissenen Auftrag und damit einen übersichtlicheren Kontext. Kleine Aufträge gelingen erfahrungsgemäß häufiger als große.
- Spezialisierung: Analyse, Implementierung und Prüfung können unterschiedliche Agenten, Modelle oder Werkzeuge übernehmen.
Der Haken: Software ist kein Stapel unabhängiger Blätter. Teile teilen Schnittstellen, Datenmodelle, Build-Konfiguration und Datenbankmigrationen. Sobald zwei Agenten dieselbe Stelle anfassen oder sich auf unterschiedliche Annahmen über eine gemeinsame Schnittstelle stützen, entsteht Integrationsarbeit. Im schlimmsten Fall ist sie größer als der Zeitgewinn. Die Parallel-Roadmap der Fabrik nennt genau das als Hauptrisiko: Integrationskosten können den Parallelitätsgewinn übersteigen.
7.2 Das Grundproblem: geteilter Zustand
Stell dir zwei Agenten vor, die im selben Arbeitsverzeichnis arbeiten. Agent A ändert eine Klasse und ist halb fertig. Agent B führt in diesem Moment die Tests aus, sieht einen Kompilierfehler in A's halbfertigem Code und „repariert“ ihn. Dann committet B mit git add -A und nimmt A's halbe Arbeit mit. Danach weiß niemand mehr, wer welche Zeile verursacht hat. Ein Review, ein Warum-Trace oder eine Attestierung würden Aussagen über einen Stand treffen, den kein einzelner Agent zu verantworten hat.
Das ist kein Randfall, sondern die zwangsläufige Folge von mehreren Schreibern auf einem gemeinsamen Zustand. Menschliche Teams lösen das, indem jede Person auf ihrer eigenen Arbeitskopie arbeitet und Änderungen über Branches und Merges zusammenführt. Für Agenten gilt dieselbe Lösung, nur konsequenter, weil Agenten sich nicht zwischendurch absprechen.
Single Writer per Workspace
Zu jedem Zeitpunkt schreibt in einem Arbeitsverzeichnis genau ein Agent. Parallelität entsteht durch mehr Arbeitsverzeichnisse, nicht durch mehr Schreiber pro Verzeichnis.
Diese Regel (in der Fabrik ADR-0015) klingt streng, vereinfacht aber alles Weitere: Jeder Stand ist einem Agenten zurechenbar, Konflikte entstehen erst zwischen abgeschlossenen Branches, und beide Seiten eines Konflikts sind in sich konsistent. Ein Merge-Konflikt zwischen zwei fertigen Branches lässt sich analysieren und als Arbeitsauftrag formulieren. Ein Konflikt mitten in einem gemeinsamen Verzeichnis nicht.
Lesende Agenten sind von der Regel nicht betroffen. Reviewer und Analyse-Agenten, die nichts schreiben, dürfen beliebig parallel auf denselben Stand schauen.
Git-Worktrees als Werkzeug
Technisch bietet Git mit Worktrees ein passendes Mittel: Mehrere Arbeitsverzeichnisse teilen sich ein Repository, jedes auf einem eigenen Branch. Das spart Platz gegenüber vollständigen Klonen, und alle Branches sind sofort im gemeinsamen Objektspeicher sichtbar, was die spätere Zusammenführung vereinfacht.
In der Fabrik
Die Workflow-Ebene über dem Run
Ohne Workflow-Ebene gilt in der Fabrik: ein Run je Projekt-Workspace zur gleichen Zeit (Kapitel 6). Für Parallelität gibt es darüber die Workflow-Ebene:
- Einschalten: Feature-Flag
softwarefabrik.workflow.enabled, Voreinstellung aus. Ist es aus, verhält sich die Plattform exakt wie vorher; die UI unter/workflowszeigt dann einen erklärenden Hinweis. - Aufbau: Ein Workflow bildet ein Vorhaben ab und zerfällt in Tasks mit Abhängigkeiten. Jeder Task läuft als eigenständiger Child Run in einem eigenen Git-Worktree unterhalb von
<workspace.root>/worktrees/, höchstens drei gleichzeitig je Workflow. - Architektur: Die Workflow-Ebene ist ein eigener Slice mit der Richtung
workflow → run, niemals umgekehrt (ADR-0014). Der bestehende Run bleibt die atomare Ausführungseinheit und weiß nicht, dass er Teil eines Workflows ist. - Run-Sperre je Workspace: Die Sperre vergleicht den effektiven Workspace, nicht pauschal das Projekt. Getrennte Worktrees dürfen parallel laufen, dasselbe Verzeichnis weiterhin nicht.
- Rollen: Lesen für alle Rollen; Ändern, Starten und Umplanen ab
DEVELOPER; Planfreigabe, Integration und Konfliktentscheidung abMAINTAINER. Workflows erben den Mandanten ihres Projekts, fremde Mandanten sehen „nicht gefunden“. - Budget: Ein Workflow-Budget greift vor dem Start des nächsten Tasks.
Ein Workflow durchläuft fachliche Zustände wie PLANNING, WAITING_FOR_PLAN_APPROVAL, RUNNING, INTEGRATING, WAITING_FOR_APPROVAL und COMPLETED. Kein Agent startet, bevor der Plan freigegeben ist.
7.3 Zerlegen: der Abhängigkeitsgraph
Bevor irgendetwas parallel laufen kann, muss das Vorhaben zerlegt werden. Das Ergebnis ist ein gerichteter, azyklischer Graph (englisch DAG): Knoten sind Aufgaben, Kanten sagen „B kann erst beginnen, wenn A fertig ist“. Parallel laufen dürfen genau die Aufgaben, die in diesem Graphen nicht voneinander abhängen und deren Vorgänger fertig sind.
Arten von Abhängigkeiten
Nicht jede Abhängigkeit ist „erst A, dann B“. Die Parallel-Roadmap der Fabrik unterscheidet zum Beispiel:
- Fertigstellung: B braucht das Ergebnis von A.
- Vertrag: B braucht eine festgelegte Schnittstelle, nicht A's Implementierung.
- Artefakt: B braucht ein bestimmtes Erzeugnis, etwa eine generierte Datei.
- Freigabe: B darf erst nach einer menschlichen Entscheidung beginnen.
- Exklusive Ressource: A und B fassen dieselbe Datei an, die keine zwei gleichzeitigen Änderungen verträgt.
Die Unterscheidung lohnt sich: Eine Vertragsabhängigkeit lässt sich auflösen, indem man den Vertrag zuerst festlegt. Danach können Anbieter und Nutzer der Schnittstelle gleichzeitig entstehen. Eine Fertigstellungsabhängigkeit lässt sich nicht so einfach auflösen.
Gute Zerlegung
- Nach Schreibbereichen schneiden, nicht nach Fachbegriffen. Zwei Aufgaben, die dieselben Dateien ändern, sind nicht parallel, egal wie verschieden sie fachlich klingen.
- Gemeinsames zuerst: Datenmodell, Schnittstellen, Migrationen und Build-Änderungen in eine frühe, eigene Aufgabe legen.
- Klein, aber vollständig: Jede Aufgabe soll für sich bauen und testbar sein.
- Analyse vor Umsetzung: Lesende Analyse-Aufgaben sind ideal für Parallelität, weil sie nichts schreiben. Ihre Ergebnisse fließen in die Planung der schreibenden Aufgaben.
Wie du allgemein plant und Akzeptanzkriterien je Aufgabe festlegst, behandelt Kapitel 5.
In der Fabrik
Analyse-Tasks, Synthese und Capabilities
Die Workflow-Ebene ist schrittweise entstanden; der erste Schritt (v0.21) war parallele read-only Analyse:
- Mehrere Analyse-Läufe laufen gleichzeitig, jeder in eigenem Worktree, koordiniert über einen Task-Graphen.
- Ein Synthese-Schritt hängt automatisch an allen Tasks, baut seinen Auftrag aus deren Ergebnissen und hält fest, welche Eingaben eingeflossen sind. Der Auftrag fordert Widersprüche ausdrücklich ein und verbietet das Glätten.
- Der Warum-Trace eines Child Runs zeigt seinen Workflow-Kontext.
Seit v0.22 sind auch schreibende Capabilities wählbar. Die Roadmap klassifiziert Tasks nicht nach festen Rollen, sondern nach Fähigkeiten, etwa Implementierung von Domäne, API, Persistenz, Frontend oder Tests (schreibend), Anforderungs-, Architektur- oder Sicherheitsanalyse (analysierend) und Code-, Architektur- oder Sicherheitsreview (prüfend). Tasks legst du unter /workflows/{id} an, inklusive Ziel, Capability und Abhängigkeiten.
7.4 Verträge vor paralleler Implementierung
Wenn Frontend und Backend gleichzeitig entstehen, müssen beide wissen, wie die Schnittstelle zwischen ihnen aussieht. Lässt man jeden Agenten seine eigene Annahme treffen, entstehen zwei Builds, die jeweils grün sind und zusammen nicht funktionieren. Kein Test einer einzelnen Änderung zeigt auf dieses Problem, denn der Bruch liegt zwischen den Komponenten.
Daher das Prinzip Contracts before Parallel Implementation: Voneinander abhängige Komponenten dürfen erst parallel implementiert werden, wenn der gemeinsame Vertrag versioniert vorliegt. Verträge können sein:
- API-Beschreibungen (OpenAPI für REST, AsyncAPI für Ereignisse),
- Datenschemas (JSON-Schema, DTO-Definitionen),
- Java- oder TypeScript-Interfaces,
- Domänenereignisse,
- Akzeptanzkriterien.
Drei Eigenschaften eines brauchbaren Vertrags
- Unveränderliche Fassungen. Eine Änderung erzeugt eine neue Fassung, die alte bleibt bestehen. Sonst verweist der Nachweis „Agent B hat gegen Fassung 3 gebaut“ auf einen Inhalt, der sich inzwischen geändert haben könnte.
- Inhaltsprüfsumme. Ein Hash über den Inhalt macht Fassungen eindeutig vergleichbar.
- Bindung zum richtigen Zeitpunkt. Welche Fassung gilt, wird festgeschrieben, wenn die Arbeit beginnt, nicht schon bei der Planung. Zwischen Planung und Start kann sich der Vertrag noch ändern.
Wenn sich der Vertrag ändert
Ändert sich ein Vertrag, während Aufgaben gegen die alte Fassung arbeiten, sind diese Aufgaben veraltet. Ihr Ergebnis darf nicht ungeprüft übernommen werden, auch wenn ihr eigener Build grün ist. Die Aufgabe muss neu bewertet werden: erneut gegen die neue Fassung ausführen oder verwerfen.
In der Fabrik
Die Contract Registry
Unter /vertraege liegt die Contract Registry (seit v0.23):
- Unveränderliche Fassungen mit Nummer und Content-Hash (SHA-256 über den normalisierten Inhalt). Die Normalisierung ist bewusst minimal: Zeilenenden, abschließende Leerzeichen, Datei-Endzeilenumbruch. Eine Umformatierung gilt als Änderung. Ein falsches „veraltet“ kostet eine erneute Prüfung, ein übersehenes einen stillen Vertragsbruch.
- Bindung beim Start: Ein Task erklärt im Plan nur den Namen des Vertrags, den er benutzt (Vertragsformular am Task unter
/workflows/{id}). Die konkrete Fassung wird beim Start des Child Runs festgeschrieben. - Stale-Erkennung: Eine neue Fassung setzt jeden daran gebundenen, nicht abgeschlossenen Task auf
WAITING_FOR_REPLAN, synchron in derselben Transaktion. Auch ein Task, dessen Vertrag sich während seines Laufs ändert, wird nicht aufPASSEDgehoben. - Merge-Blockade: Vor dem Einreihen in die Merge Queue und vor dem Merge wird geprüft, ob die gebundene Fassung noch aktuell ist.
- Auflösung durch den Menschen: erneut gegen die neue Fassung ausführen oder verwerfen. Ein Workflow mit veralteten Tasks geht nach
WAITING_FOR_APPROVAL, statt still aufRUNNINGstehenzubleiben. - Prüfung: Strukturprüfung für OpenAPI, AsyncAPI und JSON-Schema; ein nicht parsebarer Vertrag ist
CRITICAL. Breaking-Change-Erkennung zwischen zwei Fassungen: entfernte Pfade, Kanäle, Operationen, Felder und Schemata sowie neue Pflichtfelder sindHIGH, rein additive ÄnderungenINFO. Befunde laufen als normaleReviewFindings. - Nicht blockierend: Eine brechende Änderung ist manchmal genau das Ziel. Das Integration Gate stuft deshalb von
PASSEDaufWARNINGherunter, statt zu scheitern.
$ref-Verweisen und versteht keine Typverengungen (etwa string zu enum). Consumer-/Provider-Vertragstests sind nicht enthalten.
7.5 Besitz und Leases
Single Writer pro Verzeichnis verhindert, dass zwei Agenten sich gegenseitig die Arbeitskopie zerschießen. Es verhindert nicht, dass zwei Agenten in ihren getrennten Worktrees dieselbe Datei ändern. Das merkt man erst beim Zusammenführen, und dann ist die Arbeit schon getan. Besser ist, Überschneidungen vor dem Start zu erkennen.
Erklärter Besitz
Dafür erklärt jede schreibende Aufgabe, welche Bereiche sie ändern will (Schreibbereiche), welche sie nur liest und welche sie nicht anfassen darf. Überschneiden sich die Schreibbereiche zweier aktiver Aufgaben, startet die zweite nicht. Bestimmte Dateien sind grundsätzlich exklusiv, weil zwei gleichzeitige Änderungen dort fast sicher kollidieren: zentrale Build-Konfiguration, Datenbankmigrationen, gemeinsame API-Spezifikationen.
Leases statt ewiger Sperren
Ein Besitzanspruch braucht ein Ablaufdatum. Stürzt ein Agentenprozess ab, darf seine Sperre den Rest des Vorhabens nicht dauerhaft blockieren. Daher vergibt man Besitz als Lease: einen Anspruch mit Laufzeit, der per Herzschlag verlängert wird. Bleibt der Herzschlag aus, gilt der Anspruch als verwaist und kann freigegeben werden.
Ein verwandtes Problem tritt bei mehreren ausführenden Prozessen auf: Zwei Worker könnten gleichzeitig dieselbe Aufgabe übernehmen. Die Lösung ist ein atomarer Anspruch, bevor irgendetwas geschieht: Nur einer kann ihn gewinnen. Und wenn ein Worker ausfällt, gilt: Eine Aufgabe, die noch nicht lief, darf zurück in die Warteschlange. Eine, die schon lief, hat einen Arbeitsstand unbekannter Qualität hinterlassen und darf nicht still wiederholt werden.
In der Fabrik
Pfad-Besitzmodell, Leases und Koordinationsschicht
- Drei Bereichsarten je Task:
OWNED(Schreibbereiche),READ_ONLY(Lesebereiche),PROTECTED(Schutzzonen). Du erklärst sie am Task unter/workflows/{id}; die Seite zeigt die Schreibbereiche je Task. - Prüfung vor dem Start: Überschneiden sich die Bereiche mit denen eines aktiven Tasks, startet der Task gar nicht erst. Verglichen wird auf Segmentgrenzen, damit
src/mainundsrc/mainxnicht fälschlich kollidieren. - Immer exklusiv: u. a. Build-Konfiguration (
pom.xml,build.gradle,build.gradle.kts), das Migrationsverzeichnis,openapi.yamlunddocker-compose.yml, auch wenn ein Task sie nicht anmeldet. - Leases mit Laufzeit:
softwarefabrik.workflow.lease-laufzeit-minuten(Vorgabe 120), verlängert per Herzschlag. Ein abgestürzter Agent legt den Workflow damit nicht dauerhaft lahm; sein Lease gilt als verwaist und wird beim nächsten Erwerb freigegeben. - Koordinationsschicht (v0.26): Worker registrieren sich mit Herzschlag und beanspruchen einen Task atomar, bevor Worktree und Child Run entstehen. Verfällt der Anspruch auf einen Task, der noch nicht lief, geht er zurück nach
READY; lief er bereits, geht er nachFAILED. Der Weg zurück führt über das Replanning mit Begründung. - Automatischer Versand:
softwarefabrik.workflow.dispatcher.enabled(Default aus) startet freigewordene Nachfolger von selbst. Automatisches Starten kostet Tokens, deshalb schaltest du es bewusst ein.
<workspace.root>/worktrees/. Sie werden nach Abschluss entfernt; nach einem harten Abbruch kann git worktree prune im Projekt-Workspace nötig sein.
7.6 Zusammenführen: Reihenfolge, Konflikte, Integration
Wenn alle parallelen Aufgaben fertig sind, liegen mehrere Branches vor, die jeweils für sich gebaut und geprüft wurden. Jetzt kommt der schwierigste Teil: Sie zu einem Gesamtstand zusammenzuführen, der als Ganzes funktioniert.
Feste Reihenfolge
Man könnte Branches in der Reihenfolge zusammenführen, in der sie fertig werden. Dann hängt das Ergebnis aber an zufälligen Laufzeiten, und zwei Durchläufe desselben Plans ergeben unterschiedliche Stände. Reproduzierbar wird es erst mit einer festen Reihenfolge, etwa der Planreihenfolge, und einer sequenziellen Warteschlange (Merge Queue).
Konflikte sind nicht gleich Konflikte
Für Git sieht jeder Konflikt gleich aus: zwei Seiten, die sich an derselben Stelle widersprechen. In der Praxis verlangen sie sehr verschiedene Reaktionen:
- Zwei Migrationen mit derselben Nummer: umnummerieren, aber sorgfältig, weil Datenbankstände daran hängen.
- Zwei hinzugefügte Abhängigkeiten in der Build-Datei: meist mechanisch lösbar.
- Reine Formatierungsunterschiede: harmlos.
- Ein veralteter Branch: ein Rebase auf den aktuellen Stand kann genügen.
- Ein echter inhaltlicher Widerspruch: Hier muss jemand entscheiden, was gelten soll.
Ein Agent, dem man nur „löse den Konflikt“ sagt, rät. Wer Konflikte klassifiziert, kann gezielt reagieren: mechanische Fälle automatisieren, inhaltliche eskalieren. Und ein Rebase ist kein Allheilmittel: Er löst Konflikte, die nur daher rühren, dass ein Branch alt ist, aber keine inhaltlichen Widersprüche.
Integration prüfen
Jede Aufgabe wurde für sich geprüft. Das beantwortet nicht, ob die Änderungen zusammen funktionieren. Deshalb braucht es nach dem Zusammenführen eine eigene Prüfung des Gesamtstands: Gesamtbuild, Integrationstests, Architekturtests über den integrierten Stand, Vertragskompatibilität. Erst wenn diese Integrationsprüfung besteht, ist das Vorhaben fertig.
In der Fabrik
Merge Queue, Integration Gate und Konfliktklassifikation
- Merge Queue (v0.22): Erfolgreiche Child-Branches werden sequenziell und in Planreihenfolge in einen Integrationsbranch
wf/<id>/integrationgeführt, nicht in der Reihenfolge, in der sie zufällig fertig wurden. - Integration Gate: Erst der zusammengeführte Gesamtstand wird geprüft. Das lokale Gate je Child Run prüft eine Änderung für sich, das Integration Gate, ob sie zusammen funktionieren. Ein Workflow erreicht
COMPLETEDnur über dieses Urteil. Integration löst du unter/workflows/{id}aus (abMAINTAINER). - Konflikt hält an: Der Workflow wechselt nach
WAITING_FOR_APPROVALund zeigt die Konfliktdateien. Zu entscheiden ist zwischen erneut versuchen (höchstens zwei Versuche) und Branch verwerfen. Ein verworfener Branch macht das GateFAILED, denn ein Workflow, dessen Arbeit teilweise liegen blieb, ist nicht erfolgreich. - Kaskaden-Abbruch: Scheitert ein Task, werden alle Tasks abgebrochen, die auch mittelbar auf ihm aufbauen, und offene Merge-Einträge verworfen.
- Konfliktklassifikation (v0.25) in sieben Arten:
MIGRATIONSNUMMER,BUILD_KONFIGURATION,SPERRDATEI,NUR_FORMATIERUNG,VERALTETER_BRANCH,INHALTLICHER_WIDERSPRUCH,UNBEKANNT. Geprüft wird von der teuersten zur harmlosesten Art. Die Analyse läuft ohne Nebenwirkung übergit merge-tree --write-tree. - Rebase vor Merge, nicht statt Merge: Er wird nur versucht, wenn die Einordnung ihn als aussichtsreich einstuft; bei
MIGRATIONSNUMMERundINHALTLICHER_WIDERSPRUCHbewusst nicht. - Eskalationsbericht: nennt Branch, Task, Capability, Konfliktart, Dateien, bisherige Versuche, Rebase-Ergebnis, beteiligte Vertragsfassungen und die Empfehlung. Die Einordnung wird am Queue-Eintrag festgehalten.
7.7 Umplanen mit Grund
Kein Plan überlebt die Ausführung unverändert. Eine Aufgabe erweist sich als zu groß, ein Vertrag ändert sich, ein Konflikt zeigt, dass zwei Aufgaben eigentlich eine sind. Umplanen ist also normal. Gefährlich wird es, wenn es spurlos passiert oder als Vorwand für blinde Wiederholungen dient.
Zwei Regeln helfen:
- Jede Planänderung ist versioniert und begründet. Nicht nur „Plan Version 4“, sondern: Was hat sich geändert, warum, wer hat es ausgelöst? Die Frage, die Wochen später kommt, lautet nicht „welche Version“, sondern „warum diese“.
- Ein Task wird nur mit neuer Eingabe neu gestartet. Das ist dieselbe Regel wie bei Korrekturschleifen in Kapitel 6. Ein anderer Zuschnitt oder eine neue Reihenfolge ordnen Arbeit um, geben dem Agenten aber nichts, was er beim letzten Versuch nicht hatte.
In der Fabrik
PlanRevision, Kennzahlen und Spec-Bindung je Task
Versionierte Planänderungen (v0.24)
PlanRevisionhält jede Änderung unveränderlich fest: Version, Grund (Kategorie aus einer festen Liste), Pflicht-Beschreibung, Auslöser, betroffene Tasks. Attestiert alsWORKFLOW_REPLANNED.- AP-6 hart erzwungen: Ein Task wird nur zurückgesetzt, wenn der Grund eine neue Eingabe trägt: Vertragsänderung, Merge-Konflikt, Build-Fehler, Reviewer-Befund, gescheiterter Task oder menschliche Anweisung.
- Plan-Operationen unter
/workflows/{id}: Task aufteilen (Teile erben Abhängigkeiten und Capability, nicht die Schreibbereiche), Tasks zusammenführen (der neue Task erbt die Vereinigung der Schreibbereiche), Reihenfolge ändern. Ein laufender Task lässt sich nicht umschneiden. - Der Warum-Trace zeigt die Planhistorie mit Gründen.
Kennzahlen (v0.27)
Unter /kennzahlen misst die Fabrik ohne eigenes Schema, abgeleitet aus vorhandenen Daten: Time to Accepted Merge, Erstdurchlauf-Quote, Korrekturschleifen, Planänderungen, Kosten je Workflow und Child Run, Merge-Konfliktquote nach Konfliktart, Freigabe-Wartezeit und Eingriffe (inklusive Planfreigabe). Was sich nicht ehrlich messen lässt, etwa menschliche aktive Arbeitszeit, wird als Lücke ausgewiesen; eine Quote ohne Nenner erscheint als Strich, nicht als 0 %.
Spec-Bindung je Task (v0.36)
Parallele Agenten müssen nicht nur gegen dieselbe Schnittstelle, sondern auch gegen dieselben Anforderungen arbeiten:
- Ein Task deklariert Spec und AC-IDs, beim Anlegen oder nachträglich über „Spec-Bezug erklären“ (
/workflows/{id}/tasks/{taskId}/spec, abDEVELOPER, mandantengeprüft). - Beim Start des Child Runs wird die zu diesem Zeitpunkt freigegebene Fassung am Task gebunden (Version, Hash, AC-IDs) und als
WORKFLOW_TASK_SPEC_GEBUNDENattestiert. Alle Child Runs eines Workflows arbeiten gegen dieselbe Fassung, auch wenn die Spec zwischenzeitlich neu freigegeben wurde. Der Child Run bekommt die gebundenen Kriterien im Objective-Kopf und im ReviewRequest. - Im Spec-Modus
STRENGlehnt die Planfreigabe ab, wenn ein schreibender Task keinen Spec-Bezug auf eine freigegebene Fassung hat. - Jede Spec-Freigabe spiegelt den Akzeptanzkriterien-Block als Vertrag
SPEC-<nnn>-acder ArtACCEPTANCE_CRITERIAin die Contract Registry; ein unveränderter Block erzeugt keine neue Fassung. - Seit v0.38.0 (SPEC-003): Die Registry vergleicht
ACCEPTANCE_CRITERIA-Verträge auf AC-Ebene und meldet entfernte Kriterien als Bruch. Ändert oder entfernt eine neu freigegebene Spec-Fassung ein Kriterium, setzt die Fabrik daran gebundene, nicht abgeschlossene Workflow-Tasks aufWAITING_FOR_REPLAN(Anzeige unter „Veraltete Grundlage“) und markiert gebundene offene Backlog-Einträge als veraltet (Spec-Drift,SPEC_DRIFTin der Audit-Kette).
Mehr zu Specs, Freigabe und Spec-Modi in Kapitel 4.
7.8 Wann du nicht parallelisieren solltest
Parallelität ist ein Werkzeug, kein Ziel. Verzichte darauf, wenn:
- die Aufgaben dieselben Dateien ändern, etwa einen zentralen Service oder ein gemeinsames Datenmodell. Die Besitzprüfung würde sie ohnehin nacheinander starten.
- noch kein Vertrag existiert und die Schnittstelle erst beim Bauen entsteht. Lege dann zuerst eine Aufgabe „Vertrag festlegen“ an.
- das Vorhaben klein ist. Planung, Freigabe, Integration und Prüfung kosten Zeit; für eine Änderung, die ein einzelner Run in einem Durchgang schafft, lohnt sich ein Workflow nicht.
- die Anforderungen unklar sind. Drei Agenten, die eine vage Anforderung unterschiedlich interpretieren, erzeugen drei inkonsistente Teile. Kläre erst die Spec.
- niemand die Integration verantwortet. Konflikte und veraltete Tasks brauchen menschliche Entscheidungen. Ohne jemanden, der sie trifft, bleibt der Workflow stehen.
Übung
Übung: Ein Vorhaben in einen Workflow zerlegen
Vorhaben: „Kunden sollen Rechnungen als PDF herunterladen können. Dazu braucht es einen REST-Endpunkt, eine PDF-Erzeugung im Backend, eine neue Spalte für den Ablageort in der Datenbank und einen Download-Button im React-Frontend.“
- Zeichne den Abhängigkeitsgraphen mit vier bis sechs Tasks. Markiere je Kante, ob es eine Fertigstellungs-, Vertrags- oder Ressourcenabhängigkeit ist.
- Lege fest, welcher Vertrag zuerst existieren muss, damit Frontend und Backend parallel entstehen können, und in welchem Format (z.B. OpenAPI).
- Notiere je schreibendem Task
OWNED-,READ_ONLY- undPROTECTED-Bereiche. Welcher Task bekommt die Migration, und warum darf sie nur einer haben? - Überlege, welche Konfliktart der Fabrik-Klassifikation am wahrscheinlichsten auftritt und wie du sie vermeidest.
- Wenn du Zugang zu einer Instanz mit
softwarefabrik.workflow.enabled=truehast: Lege den Workflow unter/workflowsmit dem Mock-Adapter an, erkläre Schreibbereiche und den Vertrag, gib den Plan frei und beobachte Merge Queue und Integration Gate.
Lösungsskizze
Task 1: Vertrag festlegen (OpenAPI für GET /rechnungen/{id}/pdf), schreibt nur die Vertragsdatei. Task 2: Migration und Persistenz (besitzt das Migrationsverzeichnis exklusiv). Task 3: PDF-Erzeugung im Backend (hängt an Task 2). Task 4: REST-Endpunkt (Vertrag aus Task 1, Fertigstellung von Task 3). Task 5: Frontend-Button (nur Vertragsabhängigkeit zu Task 1, läuft parallel zu 2 bis 4; OWNED ist das Frontend-Verzeichnis, Backend ist READ_ONLY). Wahrscheinlichster Konflikt: BUILD_KONFIGURATION, wenn Task 3 und Task 5 Abhängigkeiten hinzufügen; vermeidbar, indem nötige Bibliotheken in einer frühen Aufgabe ergänzt werden. Die Migration gehört genau einem Task, weil die Fabrik das Migrationsverzeichnis ohnehin exklusiv behandelt und konkurrierende Nummern als MIGRATIONSNUMMER-Konflikt enden würden.
Kapitelfragen
-
Was besagt das Prinzip „Single Writer per Workspace“, und welches Problem verhindert es?
Musterantwort
In einem Arbeitsverzeichnis schreibt zu jedem Zeitpunkt genau ein Agent; Parallelität entsteht durch mehr Arbeitsverzeichnisse. Es verhindert, dass ein Agent die halbfertige Arbeit eines anderen mitcommittet oder „repariert“, wodurch Stände niemandem mehr zurechenbar wären und Review, Warum-Trace und Attestierung ihre Aussagekraft verlören.
-
Warum dürfen Reviewer und Analyse-Agenten beliebig parallel laufen, schreibende Tasks aber nicht?
Musterantwort
Lesende Agenten verändern keinen Zustand, also können sie sich nicht gegenseitig stören oder Arbeit vermischen. Schreibende Agenten erzeugen Änderungen, die zurechenbar bleiben und kontrolliert zusammengeführt werden müssen; deshalb bekommen sie eigene Worktrees und erklärte Schreibbereiche.
-
Erkläre „Contracts before Parallel Implementation“ an einem Beispiel aus Frontend und Backend.
Musterantwort
Bevor Frontend-Formular und Backend-Endpunkt parallel entstehen, wird die Schnittstelle als versionierter Vertrag festgelegt, etwa als OpenAPI-Dokument. Beide Agenten arbeiten gegen dieselbe, beim Start gebundene Fassung. Ohne Vertrag träfe jeder eigene Annahmen; beide Builds wären grün, zusammen funktionierten sie nicht.
-
Was passiert in der Fabrik, wenn ein Vertrag eine neue Fassung bekommt, während ein daran gebundener Task noch läuft?
Musterantwort
Die Stale-Erkennung setzt den Task auf
WAITING_FOR_REPLAN. Auch wenn sein Lauf danach erfolgreich endet, wird das Ergebnis nur festgehalten, der Task aber nicht aufPASSEDgehoben. Die Merge-Blockade verhindert die Integration, und der Workflow geht nachWAITING_FOR_APPROVAL. Ein Mensch entscheidet: erneut gegen die neue Fassung ausführen oder verwerfen. -
Warum führt die Merge Queue Branches in Planreihenfolge zusammen und nicht in der Reihenfolge, in der sie fertig werden?
Musterantwort
Sonst hinge das integrierte Ergebnis an zufälligen Laufzeiten, und zwei Durchläufe desselben Plans könnten unterschiedliche Stände und Konflikte erzeugen. Die feste Reihenfolge macht die Integration reproduzierbar und nachvollziehbar.
-
Ein Agent schlägt vor, jeden Merge-Konflikt per Rebase zu lösen. Was antwortest du?
Musterantwort
Ein Rebase löst nur Konflikte, die daher rühren, dass ein Branch veraltet ist. Er löst keine inhaltlichen Widersprüche und verschiebt Migrationsnummer-Kollisionen nur. Deshalb klassifiziert die Fabrik Konflikte zuerst und versucht den Rebase nur, wenn die Einordnung ihn als aussichtsreich einstuft; bei
MIGRATIONSNUMMERundINHALTLICHER_WIDERSPRUCHbewusst nicht. -
Wozu braucht man ein Integration Gate, wenn jeder Child Run bereits sein eigenes Gate bestanden hat?
Musterantwort
Das lokale Gate prüft eine Änderung für sich. Ob mehrere Änderungen zusammen funktionieren, zeigt erst die Prüfung des integrierten Gesamtstands. In der Fabrik erreicht ein Workflow
COMPLETEDnur über das Urteil des Integration Gates; ein verworfener Branch macht esFAILED, ein Vertragsbruch stuft aufWARNINGherunter. -
Jemand möchte einen gescheiterten Task einfach „noch mal laufen lassen“. Unter welcher Bedingung erlaubt die Fabrik das, und warum?
Musterantwort
Nur über ein Replanning, dessen Grund eine neue Eingabe trägt, etwa Build-Fehler, Reviewer-Befund, Merge-Konflikt, Vertragsänderung oder eine menschliche Anweisung. Die Änderung wird als
PlanRevisionmit Beschreibung festgehalten und alsWORKFLOW_REPLANNEDattestiert. Grund ist AP-6: Eine Wiederholung ohne neue Information kostet Tokens, ohne die Erfolgschance gezielt zu erhöhen. -
Transfer: Drei Child Runs eines Workflows setzen Teile derselben Spec um. Während der zweite läuft, gibt eine Maintainerin eine neue Spec-Fassung frei. Gegen welche Fassung arbeiten die drei Runs, und warum ist das gewollt?
Musterantwort
Gegen die Fassung, die beim Start am Task gebunden wurde (
WORKFLOW_TASK_SPEC_GEBUNDEN); alle Child Runs eines Workflows arbeiten nachweislich gegen dieselbe Fassung. Das verhindert, dass parallele Teile gegen unterschiedliche Anforderungen gebaut werden und erst bei der Integration auseinanderfallen. Die neue Fassung wird bewusst über eine neue Planung bzw. einen neuen Workflow übernommen; seit v0.38.0 setzt die Spec-Drift-Erkennung betroffene, nicht abgeschlossene Tasks aufWAITING_FOR_REPLAN. -
Nenne drei Situationen, in denen du auf einen Workflow verzichtest und einen einzelnen Build-Run vorziehst.
Musterantwort
Wenn die Teilaufgaben dieselben Dateien ändern, wenn das Vorhaben so klein ist, dass Planung und Integration mehr kosten als die Parallelität bringt, und wenn Anforderungen oder Schnittstellen noch unklar sind. Auch ohne verantwortliche Person für Integrationsentscheidungen bleibt ein Workflow stehen.