{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/root\/.claude\/plans\/cosmic-wibbling-crayon.md",
"content": "# Pipeline Live-Status-Seite - Implementierungsplan\n\n## Zusammenfassung\n\nNach Klick auf \"Pipeline starten\" wird auf eine dedizierte Status-Seite weitergeleitet, die in Echtzeit zeigt:\n- Aktueller Pipeline-Schritt (detect, extract, chunk, embed, analyze)\n- Aktuelles Dokument (Dateiname)\n- Fortschritt (X\/Y Dokumente, Z Chunks)\n- Zeit (vergangen + geschätzt verbleibend)\n- Live-Log der letzten 20 Zeilen\n- Stall-Erkennung (>60s keine Aktivität)\n- Abbruch-Möglichkeit\n\n---\n\n## 1. Datenbank-Schema (ki_content.pipeline_runs)\n\n```sql\nALTER TABLE pipeline_runs\nADD COLUMN current_step VARCHAR(50) NULL AFTER error_log,\nADD COLUMN current_document VARCHAR(500) NULL AFTER current_step,\nADD COLUMN last_update_at DATETIME NULL AFTER current_document,\nADD COLUMN embeddings_created INT DEFAULT 0 AFTER chunks_created,\nADD COLUMN log_tail TEXT NULL AFTER last_update_at,\nADD INDEX idx_status_last_update (status, last_update_at);\n```\n\n---\n\n## 2. Python Pipeline Modifikationen\n\n### 2.1 Argument Parsing (`pipeline.py`)\n```python\nimport argparse\n\ndef parse_args():\n parser = argparse.ArgumentParser()\n parser.add_argument('command', choices=['scan', 'process', 'embed', 'all', 'file', 'status'])\n parser.add_argument('--pipeline-id', type=int)\n parser.add_argument('--run-id', type=int)\n parser.add_argument('file_path', nargs='?')\n return parser.parse_args()\n```\n\n### 2.2 Progress-Tracker Klasse (`db.py`)\n- `PipelineProgress(run_id)` - Klasse für Fortschrittsaktualisierung\n- `update_step(step_name)` - Aktueller Schritt\n- `update_document(filename)` - Aktuelles Dokument\n- `update_progress(processed, total, chunks, embeddings)` - Numerische Werte\n- `add_log(message)` - Log-Zeile hinzufügen (max 20 Zeilen)\n- `complete(status, error)` - Run abschließen\n\n### 2.3 Integration in `run_full_pipeline()`\n- Bei jedem Schritt `progress.update_step()` aufrufen\n- Bei jedem Dokument `progress.update_document()` + `progress.add_log()`\n- Nach Verarbeitung `progress.update_progress()` mit neuen Werten\n- Bei Fehler\/Abschluss `progress.complete()`\n\n---\n\n## 3. PHP Controller Änderungen\n\n### Neue Routes (`routes\/web.php`)\n```php\n$router->get('\/content-pipeline\/{id}\/run\/{runId}\/status', [ContentPipelineController::class, 'runStatus']);\n$router->get('\/content-pipeline\/{id}\/run\/{runId}\/status\/poll', [ContentPipelineController::class, 'runStatusPoll']);\n$router->post('\/content-pipeline\/{id}\/run\/{runId}\/cancel', [ContentPipelineController::class, 'runCancel']);\n```\n\n### Neue Methoden (`ContentPipelineController.php`)\n- `runStatus()` - Status-Seite rendern\n- `runStatusPoll()` - JSON-Endpoint für HTMX-Polling (inkl. stall-detection, elapsed, estimated)\n- `runCancel()` - Run abbrechen (setzt status=cancelled)\n\n### Änderung `run()`\n- Nach Pipeline-Start: Redirect zu `\/content-pipeline\/{id}\/run\/{runId}\/status`\n\n### Repository Update (`PipelineRepository.php`)\n- `findRunById(int $runId)` - Einzelnen Run laden\n- `updateRun()` erweitern für neue Spalten\n\n---\n\n## 4. Neue View (`run-status.php`)\n\n### Layout\n```\n┌─────────────────────────────────────────────────────┐\n│ Pipeline Status │\n│ Schulungsunterlagen → Run #5 │\n├─────────────────────────────────────────────────────┤\n│ [🔵 Läuft] [⚠️ Keine Aktivität seit 60s] │\n├─────────────────────────────────────────────────────┤\n│ ┌──────────┐ ┌──────────────────┐ ┌───────────┐ │\n│ │📄 Schritt│ │📁 Aktuelles Dok. │ │⏱️ Zeit │ │\n│ │ Chunk │ │ Booklet_xyz.pdf │ │ 3:45 │ │\n│ │ │ │ │ │ (~2:30) │ │\n│ └──────────┘ └──────────────────┘ └───────────┘ │\n├─────────────────────────────────────────────────────┤\n│ Dokumente: 12\/24 │\n│ [████████████░░░░░░░░░░░░░] 50% │\n├─────────────────────────────────────────────────────┤\n│ ┌────────┐ ┌────────────┐ ┌────────┐ │\n│ │ 156 │ │ 142 │ │ 0 │ │\n│ │ Chunks │ │ Embeddings │ │ Fehler │ │\n│ └────────┘ └────────────┘ └────────┘ │\n├─────────────────────────────────────────────────────┤\n│ Live Log │\n│ ┌─────────────────────────────────────────────────┐ │\n│ │[03:45:12] Processing: Booklet_xyz.pdf │ │\n│ │[03:45:15] Extracting text... │ │\n│ │[03:45:18] Created 12 chunks │ │\n│ │[03:45:20] Embedding chunks... │ │\n│ └─────────────────────────────────────────────────┘ │\n├─────────────────────────────────────────────────────┤\n│ [Abbrechen] [Zurück zur Pipeline] │\n└─────────────────────────────────────────────────────┘\n```\n\n### HTMX Polling\n```html\n<div hx-get=\"\/content-pipeline\/{id}\/run\/{runId}\/status\/poll\"\n hx-trigger=\"load, every 2s\"\n hx-swap=\"none\"\n hx-on::after-request=\"updateStatus(event)\">\n```\n\n### JavaScript `updateStatus(event)`\n- Parst JSON-Response\n- Aktualisiert alle DOM-Elemente\n- Zeigt Stall-Warning bei `is_stalled: true`\n- Stoppt Polling bei `is_terminal: true`\n\n---\n\n## 5. Timeout-Handling\n\n| Situation | Erkennung | Aktion |\n|-----------|-----------|--------|\n| Python-Crash | `last_update_at` > 60s alt | Stall-Warning anzeigen |\n| Lange Verarbeitung | Normal, regelmäßige Updates | Kein Problem |\n| Manueller Abbruch | User klickt \"Abbrechen\" | status=cancelled setzen |\n| Netzwerk-Fehler | Polling schlägt fehl | Retry mit Backoff |\n\n---\n\n## 6. Kritische Dateien\n\n| Datei | Änderung |\n|-------|----------|\n| `\/opt\/scripts\/pipeline\/pipeline.py` | Argument-Parsing, Progress-Integration |\n| `\/opt\/scripts\/pipeline\/db.py` | PipelineProgress-Klasse |\n| `ContentPipelineController.php` | 3 neue Methoden + run() ändern |\n| `PipelineRepository.php` | findRunById(), updateRun() erweitern |\n| `routes\/web.php` | 3 neue Routes |\n| `run-status.php` | Neue View (neu erstellen) |\n\n---\n\n## 7. Implementierungs-Reihenfolge\n\n1. **DB-Schema** (~5 min) - ALTER TABLE ausführen\n2. **Python** (~40 min) - args parsen, Progress-Klasse, Integration\n3. **PHP Backend** (~25 min) - Controller, Repository, Routes\n4. **Frontend** (~25 min) - View, HTMX, JavaScript\n5. **Testing** (~15 min) - End-to-End Test\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/root\/.claude\/plans\/cosmic-wibbling-crayon.md",
"content": "# Pipeline Live-Status-Seite - Implementierungsplan\n\n## Zusammenfassung\n\nNach Klick auf \"Pipeline starten\" wird auf eine dedizierte Status-Seite weitergeleitet, die in Echtzeit zeigt:\n- Aktueller Pipeline-Schritt (detect, extract, chunk, embed, analyze)\n- Aktuelles Dokument (Dateiname)\n- Fortschritt (X\/Y Dokumente, Z Chunks)\n- Zeit (vergangen + geschätzt verbleibend)\n- Live-Log der letzten 20 Zeilen\n- Stall-Erkennung (>60s keine Aktivität)\n- Abbruch-Möglichkeit\n\n---\n\n## 1. Datenbank-Schema (ki_content.pipeline_runs)\n\n```sql\nALTER TABLE pipeline_runs\nADD COLUMN current_step VARCHAR(50) NULL AFTER error_log,\nADD COLUMN current_document VARCHAR(500) NULL AFTER current_step,\nADD COLUMN last_update_at DATETIME NULL AFTER current_document,\nADD COLUMN embeddings_created INT DEFAULT 0 AFTER chunks_created,\nADD COLUMN log_tail TEXT NULL AFTER last_update_at,\nADD INDEX idx_status_last_update (status, last_update_at);\n```\n\n---\n\n## 2. Python Pipeline Modifikationen\n\n### 2.1 Argument Parsing (`pipeline.py`)\n```python\nimport argparse\n\ndef parse_args():\n parser = argparse.ArgumentParser()\n parser.add_argument('command', choices=['scan', 'process', 'embed', 'all', 'file', 'status'])\n parser.add_argument('--pipeline-id', type=int)\n parser.add_argument('--run-id', type=int)\n parser.add_argument('file_path', nargs='?')\n return parser.parse_args()\n```\n\n### 2.2 Progress-Tracker Klasse (`db.py`)\n- `PipelineProgress(run_id)` - Klasse für Fortschrittsaktualisierung\n- `update_step(step_name)` - Aktueller Schritt\n- `update_document(filename)` - Aktuelles Dokument\n- `update_progress(processed, total, chunks, embeddings)` - Numerische Werte\n- `add_log(message)` - Log-Zeile hinzufügen (max 20 Zeilen)\n- `complete(status, error)` - Run abschließen\n\n### 2.3 Integration in `run_full_pipeline()`\n- Bei jedem Schritt `progress.update_step()` aufrufen\n- Bei jedem Dokument `progress.update_document()` + `progress.add_log()`\n- Nach Verarbeitung `progress.update_progress()` mit neuen Werten\n- Bei Fehler\/Abschluss `progress.complete()`\n\n---\n\n## 3. PHP Controller Änderungen\n\n### Neue Routes (`routes\/web.php`)\n```php\n$router->get('\/content-pipeline\/{id}\/run\/{runId}\/status', [ContentPipelineController::class, 'runStatus']);\n$router->get('\/content-pipeline\/{id}\/run\/{runId}\/status\/poll', [ContentPipelineController::class, 'runStatusPoll']);\n$router->post('\/content-pipeline\/{id}\/run\/{runId}\/cancel', [ContentPipelineController::class, 'runCancel']);\n```\n\n### Neue Methoden (`ContentPipelineController.php`)\n- `runStatus()` - Status-Seite rendern\n- `runStatusPoll()` - JSON-Endpoint für HTMX-Polling (inkl. stall-detection, elapsed, estimated)\n- `runCancel()` - Run abbrechen (setzt status=cancelled)\n\n### Änderung `run()`\n- Nach Pipeline-Start: Redirect zu `\/content-pipeline\/{id}\/run\/{runId}\/status`\n\n### Repository Update (`PipelineRepository.php`)\n- `findRunById(int $runId)` - Einzelnen Run laden\n- `updateRun()` erweitern für neue Spalten\n\n---\n\n## 4. Neue View (`run-status.php`)\n\n### Layout\n```\n┌─────────────────────────────────────────────────────┐\n│ Pipeline Status │\n│ Schulungsunterlagen → Run #5 │\n├─────────────────────────────────────────────────────┤\n│ [🔵 Läuft] [⚠️ Keine Aktivität seit 60s] │\n├─────────────────────────────────────────────────────┤\n│ ┌──────────┐ ┌──────────────────┐ ┌───────────┐ │\n│ │📄 Schritt│ │📁 Aktuelles Dok. │ │⏱️ Zeit │ │\n│ │ Chunk │ │ Booklet_xyz.pdf │ │ 3:45 │ │\n│ │ │ │ │ │ (~2:30) │ │\n│ └──────────┘ └──────────────────┘ └───────────┘ │\n├─────────────────────────────────────────────────────┤\n│ Dokumente: 12\/24 │\n│ [████████████░░░░░░░░░░░░░] 50% │\n├─────────────────────────────────────────────────────┤\n│ ┌────────┐ ┌────────────┐ ┌────────┐ │\n│ │ 156 │ │ 142 │ │ 0 │ │\n│ │ Chunks │ │ Embeddings │ │ Fehler │ │\n│ └────────┘ └────────────┘ └────────┘ │\n├─────────────────────────────────────────────────────┤\n│ Live Log │\n│ ┌─────────────────────────────────────────────────┐ │\n│ │[03:45:12] Processing: Booklet_xyz.pdf │ │\n│ │[03:45:15] Extracting text... │ │\n│ │[03:45:18] Created 12 chunks │ │\n│ │[03:45:20] Embedding chunks... │ │\n│ └─────────────────────────────────────────────────┘ │\n├─────────────────────────────────────────────────────┤\n│ [Abbrechen] [Zurück zur Pipeline] │\n└─────────────────────────────────────────────────────┘\n```\n\n### HTMX Polling\n```html\n<div hx-get=\"\/content-pipeline\/{id}\/run\/{runId}\/status\/poll\"\n hx-trigger=\"load, every 2s\"\n hx-swap=\"none\"\n hx-on::after-request=\"updateStatus(event)\">\n```\n\n### JavaScript `updateStatus(event)`\n- Parst JSON-Response\n- Aktualisiert alle DOM-Elemente\n- Zeigt Stall-Warning bei `is_stalled: true`\n- Stoppt Polling bei `is_terminal: true`\n\n---\n\n## 5. Timeout-Handling\n\n| Situation | Erkennung | Aktion |\n|-----------|-----------|--------|\n| Python-Crash | `last_update_at` > 60s alt | Stall-Warning anzeigen |\n| Lange Verarbeitung | Normal, regelmäßige Updates | Kein Problem |\n| Manueller Abbruch | User klickt \"Abbrechen\" | status=cancelled setzen |\n| Netzwerk-Fehler | Polling schlägt fehl | Retry mit Backoff |\n\n---\n\n## 6. Kritische Dateien\n\n| Datei | Änderung |\n|-------|----------|\n| `\/opt\/scripts\/pipeline\/pipeline.py` | Argument-Parsing, Progress-Integration |\n| `\/opt\/scripts\/pipeline\/db.py` | PipelineProgress-Klasse |\n| `ContentPipelineController.php` | 3 neue Methoden + run() ändern |\n| `PipelineRepository.php` | findRunById(), updateRun() erweitern |\n| `routes\/web.php` | 3 neue Routes |\n| `run-status.php` | Neue View (neu erstellen) |\n\n---\n\n## 7. Implementierungs-Reihenfolge\n\n1. **DB-Schema** (~5 min) - ALTER TABLE ausführen\n2. **Python** (~40 min) - args parsen, Progress-Klasse, Integration\n3. **PHP Backend** (~25 min) - Controller, Repository, Routes\n4. **Frontend** (~25 min) - View, HTMX, JavaScript\n5. **Testing** (~15 min) - End-to-End Test\n",
"structuredPatch": [],
"originalFile": null
}
}