Zum Inhalt springen
agenticbuilders

Zurück zur Übersicht

Zwei Sessions, ein Arbeitsverzeichnis

Eine zweite Session verwarf den nicht committeten Arbeitsbaum einer ersten. Warum davon nichts zurückkam, und drei Vorkehrungen.

Von Bernhard Götzendorfer

Veröffentlicht am Kategorie: Aus der Praxis

Zwei Claude-Sessions liefen im selben Repo, im selben Arbeitsverzeichnis. Die zweite hat git reset ausgeführt und die rund 103 geänderten, noch nicht committeten Dateien der ersten verworfen. Zurückgeholt wurde davon nichts. Die Agenten schreiben direkt auf die Platte, ohne git add, und was nie in der Objektdatenbank lag, findet auch kein Reflog. Drei Vorkehrungen hätten den Schaden auf eine Welle statt vier begrenzt, zwei davon sind je ein Kommando.

Was passiert ist

Am 28. Mai 2026 lief in einem Repo eine Session über vier Wellen. Eine Migration der Fehlerbehandlung, 582 umgestellte throw-Stellen, rund 480 vorher übersprungene Tests wieder aktiviert, Typecheck auf null Fehler. Nichts davon war committet, und das war so vorgesehen: die Agenten bearbeiten Dateien direkt und unstaged, der Commit kommt am Ende.

Parallel lief eine zweite Session. Kein zweiter Klon, kein zweiter Arbeitsbaum, dasselbe Verzeichnis, laut Notiz sogar dieselbe semantische Session-ID. Diese zweite Session führte git reset aus, verwarf den kompletten Arbeitsbaum mit rund 103 geänderten Dateien und begann danach ihre eigene Arbeit an anderen Routen.

Vier Wellen Arbeit waren damit weg. Wie viel davon nachgebaut wurde und wie lange das gedauert hat, steht nirgends.

Die Lernnotiz vom selben Tag hält es so fest (Konfidenz 0.85, Status verified):

"The work was UNRECOVERABLE: agents edit in-place unstaged (per orchestrator instruction), so reset/restore leaves no stash, no commit, no dangling blobs."

Die Notiz trägt als Quell-Session unknown, die zugehörige Session-Datei fehlt. Warum die zweite Session das Reset gefahren hat, steht nirgends. Mehr Details habe ich dazu nicht.

Warum davon nichts zurückkam

git hat drei Orte für Inhalt, und nur zwei davon sind Rettungsanker.

Die Objektdatenbank. git add schreibt den Dateiinhalt als Blob nach .git/objects. Ab diesem Moment existiert er, auch ohne Commit. Ein hartes Reset macht daraus einen dangling blob, und git fsck --lost-found findet ihn wieder.

Das Reflog. Jeder Commit, jeder Branch-Wechsel, jedes Reset hinterlässt dort einen Eintrag. Das ist der Fall, an den die meisten denken, wenn sie sagen, git verliere nichts.

Das Arbeitsverzeichnis. Eine geänderte Datei, die weder gestaged noch committet ist, ist für git eine Datei auf der Platte. Kein Objekt, kein Hash, kein Eintrag.

Die Agenten schrieben per Anweisung in-place und unstaged. Damit lag die Arbeit aus vier Wellen vollständig im dritten Fall. Der Satz "git verliert nichts" gilt für alles, was git jemals gesehen hat. Über deinen Arbeitsbaum sagt er gar nichts.

Nachprüfen lässt sich das in zwei Zeilen:

git reflog
git fsck --lost-found

Lag deine Arbeit nur im Arbeitsverzeichnis, sehen beide Ausgaben völlig normal aus und keine enthält sie. Das Reflog zeigt jede Position, die HEAD hatte, nicht das, was daneben auf der Platte lag. git fsck --lost-found zeigt Objekte, die an keinem Ref mehr hängen, und eine unstaged Datei war nie eines. Dann bleiben Editor-Backups, die Dateiversionen des Betriebssystems und das Terminal-Scrollback.

Der zweite Vorfall, bei dem nichts passiert ist

Drei Wochen später, anderes Repo, 21. Juni 2026. Ein Review-Agent, der per Vertrag nur lesen darf, brauchte einen Diff gegen eine Baseline. Er führte git stash aus, wechselte per git checkout auf den Basis-Ref, verglich, und stellte danach alles wieder her. Der Baum war hinterher byte-identisch, HEAD intakt, der Stash leer, 572 Tests grün.

Die Notiz dazu ist trotzdem als anti-pattern abgelegt:

"Working tree verified byte-intact, but this is a PSA-003 violation that risks data loss."

Der Rest der Notiz sagt, was der Agent stattdessen hätte tun sollen: gegen den Basis-Ref diffen oder sich die Datei per git show ausgeben lassen, niemals stash oder checkout.

Es ist also nichts passiert. Das ist ein Ergebnis, keine Eigenschaft. Es hing daran, dass in diesem Fenster kein anderer Agent dieselben Dateien anfasste.

Wie schmal das Fenster ist, zeigt ein dritter Fall vom 6. August 2026. Ein Agent in einer Welle wollte belegen, dass eine prettier-Warnung schon vor seiner Änderung bestand, und griff dafür zu git stash. Damit lagen 58 Einträge im Stash, 24 davon untracked, also die laufende Arbeit aller parallelen Agenten, an einem Ort, an dem keiner von ihnen gesucht hätte. Bis zum pop vergingen rund 40 Sekunden. In dieser Zeit schrieben drei andere Agenten weiter, git status zählte danach 63 statt 60 Einträge. Der pop ging konfliktfrei durch, weil die drei zufällig andere Dateien betrafen.

Die Ausgangsfrage brauchte den Arbeitsbaum gar nicht:

git show HEAD:pfad/zur/datei.ts | pnpm exec prettier --stdin-filepath pfad/zur/datei.ts --check

Zweimal ist nichts passiert, und beide Male hing es daran, welche Dateien die anderen Agenten zufällig gerade angefasst haben. Das ist kein Sicherheitsmodell, das ist Glück mit Protokoll.

Wie oft so etwas vorkommt

Ich führe zu jeder Session Lernnotizen in einem eigenen Verzeichnis. Der Messweg:

cd ~/Projects/vault/40-learnings
find . -name '*.md' | wc -l
grep -rilE 'git reset|git stash|uncommitted working tree|unrecoverable' --include='*.md' . | wc -l

41 von 4421 Notizen, Stand heute. 22 davon nennen git stash namentlich. Verteilt über mehr als ein Dutzend Repos und über Monate. Das sind Nennungen, keine bestätigten Vorfälle. 41 ist die Obergrenze, nicht die Zahl.

Der unangenehme Teil daran: ein Verbot im Prompt reicht nicht. In einer Session vom 16. Juli 2026 stehen drei Vorfälle, alle nach ausdrücklichem Verbot, alle vom jeweiligen Agenten selbst offengelegt und selbst zurückgenommen. Geändert hat sich danach nicht die Formulierung des Verbots, sondern die Liste der Kommandos, die der Agent gar nicht mehr ausführen kann.

Und eine Zahl aus einer Notiz vom 2. Juni 2026 erklärt, warum das kein Randfall ist: auf dem Host liefen zu dem Zeitpunkt 24 Claude-Prozesse, und das Session-Lock wurde mitten in der Session von einem Peer neu genommen.

Drei Vorkehrungen

Checkpoint-Commit nach jeder Implementierungs-Welle

Die Gegenmaßnahme steht in der Notiz zum ersten Vorfall wörtlich drin: bei geteiltem Arbeitsbaum nach jeder Implementierungs-Welle auf einem Session-Branch committen, statt einmal am Ende. Das verhindert das Reset nicht. Es begrenzt den Schaden auf die laufende Welle.

Zwei Details hängen daran, und beide haben eigene Vorfälle:

git add -A und git commit -a nehmen in einem geteilten Baum die Arbeit der anderen Session mit. Genau so ist am 2. Juni 2026 der Refactor einer Peer-Session in einen fremden Commit gerutscht, geheilt per Amend, verloren war nichts. Ein blanker git commit committet alles Gestagte, auch das, was ein anderer Agent zwischen deinem add und deinem commit gestaged hat. Die Notiz dazu steht mit Konfidenz 1.0 und zwei belegten Vorfällen in einer einzigen Session. Also Pathspecs, immer:

git switch -c session/2026-08-14-migration
git commit -m "wip(w2): checkpoint" -- src/lib/a.ts src/lib/b.ts

Neue, noch untracked Dateien kommen so nicht mit. Die stagst du einzeln mit git add pfad/zur/datei, nie mit -A.

Session-Lock am Start

Auch das steht in der Notiz zum ersten Vorfall, als Nebensatz: den Wert aus dem Session-Lock beim Start festhalten, und wenn er sich mitten in der Session ändert, hat eine andere Session übernommen. Dann nicht weiterarbeiten, sondern zuerst den Baum prüfen.

# einmal am Session-Start, den ausgegebenen Wert merken
uuidgen | tee .git/session.lock

# vor jeder Welle
cat .git/session.lock

Welcher Wert drinsteht, ist egal, er muss nur zwischen zwei Sessions verschieden sein. $$ taugt dafür nicht: jede Bash-Zeile läuft in einer neuen Shell und hat damit eine neue PID.

Das verhindert keinen Verlust. Es verkürzt die Zeit, bis du ihn bemerkst, und zusammen mit Checkpoint-Commits ist genau das der Unterschied zwischen einer verlorenen Welle und vier.

Read-only heißt diff, nicht stash

Ein Review- oder Test-Agent, der eine Baseline braucht, hat sie bereits. HEAD, jeder Ref und jeder Commit sind lesbar, ohne den Arbeitsbaum anzufassen:

git diff HEAD
git diff <basis-ref>..HEAD
git show HEAD:pfad/zur/datei.ts
git show HEAD:pfad/zur/datei.ts | pnpm exec prettier --stdin-filepath pfad/zur/datei.ts --check

Wenn ein Agent trotzdem stasht, gehört der pop in denselben Arbeitsschritt und nicht in ein Aufräumen danach. In einem Fall vom 8. Mai 2026 hatte ein Testagent eine Katalogdatei gestasht und nicht zurückgeholt. Aufgefallen ist es erst beim Aufräumen durch den Koordinator, nach dem pop standen die 204 Einträge der Datei wieder vollständig drin.

Der Fix, der die Ursache wegnimmt

Die drei Vorkehrungen oben begrenzen den Schaden. git worktree add nimmt die Ursache weg: jede Session bekommt ein eigenes Verzeichnis bei gemeinsamer Objektdatenbank, ein Reset in der einen erreicht die andere nicht mehr.

Gratis ist das nicht. Getrennte Bäume heißen getrennte Installationen und getrennte Build-Caches, und bei kurzen Sessions geht sich das oft nicht aus. Wer parallele Sessions regelmäßig fährt, hat hier trotzdem die einzige Maßnahme auf dieser Seite, die die Form des Problems ändert statt die Größe des Verlusts.

Die Falle im Sicherheitsnetz

Der naheliegende Reflex ist ein automatischer Snapshot vor jedem Dispatch. Ich hatte den, und er hat das Falsche gesichert.

Der Snapshot lief über git stash create, weil das den Arbeitsbaum nicht anfasst. Untracked Dateien waren darin nicht enthalten. Bei Wellen, die viele neue Dateien erzeugen, waren die Snapshots damit praktisch leere Backups: das Kommando lief sauber durch, die Sicherung war vorhanden, und im Ernstfall wäre sie wertlos gewesen. Aufgefallen ist es erst, als jemand die Bäume der gespeicherten Snapshots direkt aufgelistet hat.

Der offensichtliche Fix, das Flag -u anzuhängen, hat es nicht behoben. Nach der Messung auf git 2.53 nimmt git stash create das Flag entgegen und ignoriert es; nur push und save beachten es (die Doku führt -u bei push und save, nicht bei create, abgerufen am 2026-08-14). Diese Notiz steht auf Entwurf mit Konfidenz 0.6, also prüf das auf deiner Version selbst.

Was daraus bleibt: ein Backup ist erst dann eines, wenn du seinen Inhalt gelesen hast. Exit-Code 0 ist keine Sicherung.

Was die drei Maßnahmen nicht abdecken

Das sind drei Einzelmaßnahmen aus zwei Vorfällen. Sie decken genau die Vorfälle ab, aus denen sie stammen. Was sie nicht sind: ein Verfahren, das aus jedem neuen Vorfall die nächste Regel macht, die Regeln nachweisbar hält und die wieder loswird, die nicht mehr greifen. Genau darum geht es in Modul 3 im Kurs zur Multi-Agent-Orchestrierung, wenn du parallele Wellen nicht einmal überlebst, sondern regelmäßig fährst.

Was ich nicht weiß

Ob die zweite Session das Reset selbst beschlossen hat oder auf Anweisung fuhr, weiß ich nicht, und ich werde es nicht mehr herausfinden. Also ein Gegenbeispiel, damit hier niemand zu schnell aufgibt: im Juni 2026 galt ein Commit nach gründlicher Suche als verloren. Nicht auf GitLab, nicht auf GitHub, in keinem lokalen Klon, nicht im Papierkorb, nicht in der Dateisuche. Ein git pull hat ihn dann hereingezogen, weil eine andere Maschine dieselbe Arbeit unter einer anderen SHA gepusht hatte, 28 Dateien, vollständig. "Hier und jetzt nicht erreichbar" ist nicht dasselbe wie "weg". Bei einem unstaged Arbeitsbaum hilft das nichts, bei einem verschwundenen Commit sehr wohl.

Häufige Fragen

Reicht ein zweites Terminal, oder brauche ich einen zweiten Arbeitsbaum?

Einen zweiten Arbeitsbaum, siehe oben. Ein zweites Terminal teilt sich dasselbe Verzeichnis, und genau daran ist der erste Vorfall gescheitert.

Ist git stash grundsätzlich schlecht?

Nein. In einem Baum, in dem nur du arbeitest, ist es das richtige Werkzeug. Das Problem ist nicht das Kommando, sondern seine Reichweite: es greift auf den ganzen Baum zu, auch auf die Teile, die einem anderen gehören.

Wie viel Zugriff sollte ein Agent überhaupt bekommen?

Das ist die andere Hälfte derselben Frage, und sie ist in sechs Stufen vor dem Schreibzugriff ausführlicher beantwortet als hier. Kurz gesagt: der Schreibzugriff auf den Arbeitsbaum ist eine Stufe, die Kommandos, die den Baum als Ganzes bewegen, sind eine deutlich höhere. Wo der Agent dabei läuft, ist noch einmal eine eigene Frage, und dazu steht mehr in ein lokales Modell an Claude Code.

Handout und neue Beiträge per E-Mail

Kein fixer Rhythmus, keine Zweitverwertung. Es kommt eine E-Mail, wenn es etwas Brauchbares zu berichten gibt.

Mehr dazu in der Datenschutzerklärung.