[Plugin] SSE-Writer überschreibt CLAUDE.md während Git-Operationen #288

Closed
opened 2026-01-25 14:27:29 +00:00 by jack · 0 comments
Owner

Problem

Der SSE-Writer aktualisiert CLAUDE.md-Dateien kontinuierlich während einer Session. Wenn der User Git-Operationen ausführen möchte, überschreibt der SSE-Writer die Dateien und verursacht:

  1. Ungewollte Änderungen im Commit - CLAUDE.md wird nach git add modifiziert
  2. Rebase/Merge-Konflikte - CLAUDE.md-Änderungen kollidieren mit anderen Branches
  3. Staging-Chaos - Dateien ändern sich während man committed

Reproduzierbar

  1. Session mit aktiver Observation-Erfassung starten
  2. git add -A ausführen
  3. Commit-Message schreiben
  4. SSE-Writer überschreibt CLAUDE.md in der Zwischenzeit
  5. git commit → enthält ungewollte CLAUDE.md-Änderungen

Gewünschtes Verhalten

Der SSE-Writer sollte pausieren während:

  • git add bis Commit abgeschlossen
  • git rebase läuft
  • git merge läuft
  • Generell: Aktive Git-Operationen im CLI

Technische Herausforderung

Der SSE-Writer läuft als separater Prozess (spawned in session-start Hook). Er hat keine direkte Verbindung zum Hook-System das die Tool-Calls sieht.

Architektur

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Claude Code    │────▶│  Hook-Runner    │────▶│  Backend/API    │
│  (CLI)          │     │  (Tool-Hooks)   │     │  (SSE-Server)   │
└─────────────────┘     └─────────────────┘     └────────┬────────┘
                                                         │ SSE
                                                         ▼
                                               ┌─────────────────┐
                                               │  SSE-Writer     │
                                               │  (separater     │
                                               │   Prozess)      │
                                               └─────────────────┘

Problem: Hook-Runner sieht git commit → Aber wie erreicht diese Info den SSE-Writer?

Lösungsvorschläge

Option A: Neues SSE-Event writer:pause / writer:resume

Backend sendet spezielle Events an den SSE-Writer:

// Backend: Neuer Endpoint oder Event-Type
// POST /api/writer/pause oder SSE event type: "writer:pause"

interface WriterControlEvent {
  type: 'writer:pause' | 'writer:resume';
  reason?: string;
  sessionId: string;
}

// Hook-Runner sendet bei Git-Commands
async function onToolCall(tool: string, args: any) {
  if (tool === 'Bash' && isGitCommand(args.command)) {
    await fetch(`${BACKEND_URL}/api/writer/pause`, {
      method: 'POST',
      body: JSON.stringify({ sessionId, reason: 'git-operation' })
    });
  }
}

// SSE-Writer reagiert auf Event
eventSource.addEventListener('writer:pause', () => {
  this.paused = true;
});

Option B: Lock-Datei im Repo

Einfacher, aber weniger elegant:

// Hook schreibt Lock-Datei
const LOCK_FILE = '.claude-mem-writer.lock';

// Bei Git-Command Start
await writeFile(join(repoPath, LOCK_FILE), Date.now().toString());

// Bei Git-Command Ende
await unlink(join(repoPath, LOCK_FILE));

// SSE-Writer prüft vor jedem Write
if (existsSync(join(repoPath, LOCK_FILE))) {
  return; // Skip write
}

Option C: Unix-Signal (SIGUSR1/SIGUSR2)

Hook sendet Signal an SSE-Writer Prozess:

// Hook-Runner speichert Writer-PID
const writerPid = spawn(...).pid;

// Bei Git-Command
process.kill(writerPid, 'SIGUSR1'); // Pause

// Nach Git-Command
process.kill(writerPid, 'SIGUSR2'); // Resume

// SSE-Writer
process.on('SIGUSR1', () => { this.paused = true; });
process.on('SIGUSR2', () => { this.paused = false; });

Option D: WebSocket-Kanal zwischen Hook-Runner und Writer

Bidirektionale Kommunikation:

// SSE-Writer verbindet sich auch per WebSocket
const ws = new WebSocket(`${BACKEND_URL}/ws/writer-control`);

ws.on('message', (msg) => {
  const { type } = JSON.parse(msg);
  if (type === 'pause') this.paused = true;
  if (type === 'resume') this.paused = false;
});

Empfehlung

Option A (SSE-Event) ist am saubersten:

  • Nutzt bestehende SSE-Infrastruktur
  • Keine zusätzlichen Dateien oder Sockets
  • Backend kann auch andere Clients informieren
  • Klar definiertes Protokoll

Implementierungsschritte:

  1. Backend: Neuen SSE-Event-Type writer:pause / writer:resume hinzufügen
  2. Backend: Endpoint POST /api/sessions/:id/writer-control für Hook-Runner
  3. SSE-Writer: Event-Handler für writer:pause / writer:resume
  4. Hook-Runner: Bei Git-Commands den Endpoint aufrufen

Betroffene Komponenten

  • packages/backend/src/routes/ - Neuer Endpoint
  • packages/backend/src/websocket/ oder SSE-Handler - Neuer Event-Type
  • plugin/hooks/ - Writer-Control bei Git-Commands
  • SSE-Writer Client - Event-Handler

Priorität

Mittel - Stört den normalen Development-Workflow regelmäßig

## Problem Der SSE-Writer aktualisiert CLAUDE.md-Dateien kontinuierlich während einer Session. Wenn der User Git-Operationen ausführen möchte, **überschreibt der SSE-Writer die Dateien** und verursacht: 1. **Ungewollte Änderungen im Commit** - CLAUDE.md wird nach `git add` modifiziert 2. **Rebase/Merge-Konflikte** - CLAUDE.md-Änderungen kollidieren mit anderen Branches 3. **Staging-Chaos** - Dateien ändern sich während man committed ### Reproduzierbar 1. Session mit aktiver Observation-Erfassung starten 2. `git add -A` ausführen 3. Commit-Message schreiben 4. SSE-Writer überschreibt CLAUDE.md in der Zwischenzeit 5. `git commit` → enthält ungewollte CLAUDE.md-Änderungen ### Gewünschtes Verhalten Der SSE-Writer sollte **pausieren** während: - `git add` bis Commit abgeschlossen - `git rebase` läuft - `git merge` läuft - Generell: Aktive Git-Operationen im CLI ## Technische Herausforderung Der SSE-Writer läuft als **separater Prozess** (spawned in `session-start` Hook). Er hat keine direkte Verbindung zum Hook-System das die Tool-Calls sieht. ### Architektur ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Claude Code │────▶│ Hook-Runner │────▶│ Backend/API │ │ (CLI) │ │ (Tool-Hooks) │ │ (SSE-Server) │ └─────────────────┘ └─────────────────┘ └────────┬────────┘ │ SSE ▼ ┌─────────────────┐ │ SSE-Writer │ │ (separater │ │ Prozess) │ └─────────────────┘ ``` **Problem:** Hook-Runner sieht `git commit` → Aber wie erreicht diese Info den SSE-Writer? ## Lösungsvorschläge ### Option A: Neues SSE-Event `writer:pause` / `writer:resume` Backend sendet spezielle Events an den SSE-Writer: ```typescript // Backend: Neuer Endpoint oder Event-Type // POST /api/writer/pause oder SSE event type: "writer:pause" interface WriterControlEvent { type: 'writer:pause' | 'writer:resume'; reason?: string; sessionId: string; } // Hook-Runner sendet bei Git-Commands async function onToolCall(tool: string, args: any) { if (tool === 'Bash' && isGitCommand(args.command)) { await fetch(`${BACKEND_URL}/api/writer/pause`, { method: 'POST', body: JSON.stringify({ sessionId, reason: 'git-operation' }) }); } } // SSE-Writer reagiert auf Event eventSource.addEventListener('writer:pause', () => { this.paused = true; }); ``` ### Option B: Lock-Datei im Repo Einfacher, aber weniger elegant: ```typescript // Hook schreibt Lock-Datei const LOCK_FILE = '.claude-mem-writer.lock'; // Bei Git-Command Start await writeFile(join(repoPath, LOCK_FILE), Date.now().toString()); // Bei Git-Command Ende await unlink(join(repoPath, LOCK_FILE)); // SSE-Writer prüft vor jedem Write if (existsSync(join(repoPath, LOCK_FILE))) { return; // Skip write } ``` ### Option C: Unix-Signal (SIGUSR1/SIGUSR2) Hook sendet Signal an SSE-Writer Prozess: ```typescript // Hook-Runner speichert Writer-PID const writerPid = spawn(...).pid; // Bei Git-Command process.kill(writerPid, 'SIGUSR1'); // Pause // Nach Git-Command process.kill(writerPid, 'SIGUSR2'); // Resume // SSE-Writer process.on('SIGUSR1', () => { this.paused = true; }); process.on('SIGUSR2', () => { this.paused = false; }); ``` ### Option D: WebSocket-Kanal zwischen Hook-Runner und Writer Bidirektionale Kommunikation: ```typescript // SSE-Writer verbindet sich auch per WebSocket const ws = new WebSocket(`${BACKEND_URL}/ws/writer-control`); ws.on('message', (msg) => { const { type } = JSON.parse(msg); if (type === 'pause') this.paused = true; if (type === 'resume') this.paused = false; }); ``` ## Empfehlung **Option A (SSE-Event)** ist am saubersten: - Nutzt bestehende SSE-Infrastruktur - Keine zusätzlichen Dateien oder Sockets - Backend kann auch andere Clients informieren - Klar definiertes Protokoll **Implementierungsschritte:** 1. Backend: Neuen SSE-Event-Type `writer:pause` / `writer:resume` hinzufügen 2. Backend: Endpoint `POST /api/sessions/:id/writer-control` für Hook-Runner 3. SSE-Writer: Event-Handler für `writer:pause` / `writer:resume` 4. Hook-Runner: Bei Git-Commands den Endpoint aufrufen ## Betroffene Komponenten - `packages/backend/src/routes/` - Neuer Endpoint - `packages/backend/src/websocket/` oder SSE-Handler - Neuer Event-Type - `plugin/hooks/` - Writer-Control bei Git-Commands - SSE-Writer Client - Event-Handler ## Priorität Mittel - Stört den normalen Development-Workflow regelmäßig
jack changed title from [Plugin] Race Condition: CLAUDE.md writes während Git-Operationen blockieren SSE-Writer to [Plugin] SSE-Writer überschreibt CLAUDE.md während Git-Operationen 2026-01-25 14:29:53 +00:00
jack closed this issue 2026-01-25 15:30:56 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
customable/claude-mem#288
No description provided.