from pathlib import Path
import json,re,datetime,hashlib
base=Path('/opt/struktur/aa037'); cands=json.loads((base/'candidates.json').read_text()); results=json.loads((base/'results.json').read_text())
# queue 1 was run immediately before the orchestrator
allruns=[{'queue_id':1,'relative_path':'05-Ressourcen/Obsidian-Best-Practices.md','request_id':'req-6b2fa3aafb298a9a42fc0b43','status':'success','episode_uuid':'a1a58743-18cb-4ac7-8bce-c72177c27622','llm':42,'duration_ms':374000,'max_output':3344,'edge_total':10,'edge_completed':10,'max_tokens':8192,'finish_reason':'NULL','native_finish_reason':'NULL','neo4j':'completed'}]
for x in results:
r=x.get('request') or {}; t=x.get('telemetry') or {}; p=x.get('post') or {}
allruns.append({'queue_id':x['queue_id'],'relative_path':x['relative_path'],'request_id':r.get('request_id'),'status':'success' if x['outcome']=='success' else 'FAIL_STOP','episode_uuid':p.get('graphiti_episode_uuid'),'llm':r.get('llm_request_count'),'duration_ms':round(x.get('duration_s',0)*1000),'max_output':t.get('max_output'),'edge_total':t.get('edge_total'),'edge_completed':t.get('edge_completed'),'max_tokens':(t.get('max_tokens') or [None])[0],'finish_reason':'not present','native_finish_reason':'not present','neo4j':'completed' if (t.get('neo4j_commit') and t['neo4j_commit'][-1][0]=='completed') else 'not reached','error_class':'provider_rate_limit' if x['outcome']!='success' else None})
runrows='\n'.join(f"| {r['queue_id']} | `{r['relative_path']}` | {r['status']} | `{r['request_id']}` | `{r['episode_uuid'] or '—'}` | {r['llm'] or '—'} | {r['max_output'] or '—'} | {r['edge_total'] or '—'}/{r['edge_completed'] or '—'} | {r['neo4j']} |" for r in allruns)
def safe(s):
s=str(s or '').replace('|','/').replace('\n',' ')
s=re.sub(r'(user_id|api[_-]?key|token|authorization)\s*[:=]\s*[^,}\s]+',r'\1=[REDACTED]',s,flags=re.I)
return s[:180]
candrows='\n'.join(f"| {c['id']} | `{c['relative_path']}` | {c['status']} | {c['attempts']} | {c['size']} | {safe(c['last_error'])} | {c['last_error_phase'] or '—'} | NEIN |" for c in cands)
report=f'''# AA-037 – Kontrollierte Recovery der berechtigten Queues
## Ergebnis
**FAIL-STOP nach dem ersten echten Fehler.**
- erfolgreich recoverte Queues: **8**
- fehlgeschlagene Queue im AA-037-Lauf: **1**
- weitere Queues nach dem Fehler gestartet: **0**
- Queue 184 gestartet: **NEIN**
- allgemeine Recovery technisch abgeschlossen: **NEIN**
## A. Ausgangszustand
Precheck vor Queue 1:
```text
Graphiti ready=true
neo4j_ready=true
provider_ready=true
provider_rate_limited=false
active_request_count=0
processing rows=0
keine aktive Recovery
```
Produktive Patch-Hashes blieben unverändert:
```text
/app/main.py
136feaed1cc2449968fbc40329b00af86391b216d8d2bda0c21613755b63b32b
/app/llm_call_worker.py
778a22419eb059b90fa43647dc3746e75951c304687d1c352bec871323dce027
```
Queue-Zähler nach AA-036 und vor AA-037 lagen bei `done=23`, `duplicate=1`, `failed=120`, `retry=40`.
## B. Run-Block
Ursprünglicher Block:
```json
{{
"blocked": true,
"queue_id": "166",
"reason": "pilot_completed",
"run_id": "run-2026-08-18T17:00:38.900918+00:00"
}}
```
Eine globale Entsperrung wurde **nicht** vorgenommen. Der Importer prüft den Run-Block nur ohne `force_id`; die Kombination `--ids X --force-id X` erlaubt ausschließlich die exakt passende Queue und erzwingt eine Validierung der ID-Gleichheit. `--force-id` ohne passende `--ids` wird abgelehnt. Damit war die Wirkung eindeutig begrenzt; keine zweite Automation oder allgemeine Recovery wurde freigeschaltet.
Verwendeter Einzelaufruf:
```bash
python3 /opt/struktur/obsidian-graphiti-import/importer.py \\
--once --ids <QUEUE_ID> --force-id <QUEUE_ID> --limit 1 --hold-after-success
```
## C. Kandidatenauswahl
Die Orchestrator-Kandidatenliste wurde nach erfolgreichem Queue-1-Lauf read-only erstellt. Sie enthält 158 berechtigte `retry`-/`failed`-Kandidaten ohne Episode. Queue 11, Queue 184 und die fünf AA-036-Queues wurden ausgeschlossen. `done`, `duplicate`, `processing` und Datensätze mit vorhandener Episode wurden ausgeschlossen.
### Vollständige Kandidatenliste
| ID | relative_path | status | attempts | source_size | last_error | last_error_phase | Episode |
|---:|---|---|---:|---:|---|---|---|
{candrows}
## D. Tatsächlich gestartete Queues
Die ersten acht Queues wurden erfolgreich abgeschlossen:
| Queue | relative_path | Ergebnis | Request-ID | Episode-UUID | LLM-Requests | max output | Edges | Neo4j |
|---:|---|---|---|---|---:|---:|---:|---|
{runrows}
Für alle acht erfolgreichen Queues wurde `max_tokens=8192` verwendet. Es gab keine `LLM_OUTPUT_TRUNCATED`-Meldung und keinen `JSONDecodeError`.
## E. Fail-Stop Queue 17
```text
queue_id=17
relative_path=Architecture/SMA-v2-COS.md
Vorstatus=retry
Vorversuche=1
Nachstatus=failed
Nachversuche=2
request_id=req-52b794da1db59085b964d4cd
run_id=run-2026-08-18T18:24:40.423126+00:00
HTTP=500
error_phase=graphiti_processing
```
Technische Ursache:
```text
Provider-Rate-Limit: OpenRouter Free-Tier Tageslimit erreicht
Rate-Limit-Indikator: HTTP 429 innerhalb des Graphiti-Requests
```
Der rohe Providerfehler wird hier nicht wiederholt; mögliche sensible Werte wurden nicht in den Bericht übernommen.
Telemetry Queue 17:
```text
max_tokens=8192
höchste output_tokens=3833
edge_total=8
edge_completed=2
finish_reason=nicht vorhanden
native_finish_reason=nicht vorhanden
LLM_OUTPUT_TRUNCATED=nein
JSONDecodeError=nein
Neo4j-Commit=nicht erreicht
Episode=NULL
```
Der Fehler ist kein Truncation- oder JSON-Fehler. Es wurde keine automatische Wiederholung und keine nächste Queue gestartet.
## F. Queue- und Request-Endstand
Live geprüft nach Fail-Stop:
```text
done=31
duplicate=1
failed=116
retry=36
processing=0
active_request_count=0
```
Der AA-037-Lauf erzeugte neun neue Request-Zeilen: Queue 1 plus die acht Orchestrator-Läufe bis einschließlich Queue 17. Davon waren acht erfolgreich und einer mit HTTP 500 beendet.
## G. Graphiti-Health nach Fail-Stop
```text
ready=true
neo4j_ready=true
provider_ready=true
provider_rate_limited=false
active_request_count=0
recovery_required=false
maintenance_mode=false
```
Der Provider-Rate-Limit-Fehler liegt im abgeschlossenen Request vor; der aktuelle Health-Endpunkt meldet danach keinen aktiven Rate-Limit-Zustand.
## H. Finaler Run-Block
```json
{{
"blocked": true,
"queue_id": "17",
"reason": "source_failure",
"run_id": "run-2026-08-18T18:24:40.383628+00:00"
}}
```
Der Run-Block wurde nicht aufgehoben und bleibt aktiv.
## I. Ausgeschlossene Queues
```text
Queue 11: ausgeschlossen; bereits separat in AA-035 erfolgreich getestet
Queue 184: ausdrücklich zurückgestellt; nicht gestartet
Queue 13: AA-036 erfolgreich
Queue 31: AA-036 erfolgreich
Queue 69: AA-036 erfolgreich
Queue 166: AA-036 erfolgreich
Queue 182: AA-036 erfolgreich
```
Bereits `done` und `duplicate` wurden nicht erneut gestartet. Datensätze mit vorhandener Episode wurden nicht als Kandidaten verwendet.
## J. Bewertung
```text
Anzahl erfolgreich recoverter Queues: 8
Anzahl fehlgeschlagener Queues: 1
Erster echter Fehler: Queue 17, Provider-Rate-Limit, HTTP 500
Allgemeine Recovery technisch abgeschlossen: NEIN
```
Verbleibende Live-Queues:
```text
failed=116
retry=36
Summe verbleibend failed/retry=152
```
Die verbleibenden Queues dürfen wegen des Fail-Stops nicht automatisch weiterverarbeitet werden.
## K. Empfehlung für Queue 184
Queue 184 bleibt gesperrt. Vor einem separaten Test müssen mindestens das OpenRouter-Rate-Limit-Tagesfenster beziehungsweise die Provider-Berechtigung geklärt und ein neuer expliziter Einzelauftrag erteilt werden. Queue 184 darf nicht zusammen mit einer allgemeinen Recovery gestartet werden.
Für die 152 verbleibenden `failed`-/`retry`-Queues ist zunächst eine separate Freigabe nach Klärung des Provider-Limits erforderlich. Kein höheres `max_tokens`, kein Modellwechsel und keine automatische Wiederholung.
## L. Nichtänderungen
- keine Graphiti-Codeänderung
- keine Modelländerung
- keine Nginx-Änderung
- keine Dashboard-Änderung
- keine Buzz-Änderung
- keine MiroFish-Änderung
- keine Neo4j-Manipulation außerhalb erfolgreicher normaler Importläufe
- keine manuelle Queue-Statusreparatur
- keine parallele Queue-Verarbeitung
- keine allgemeine Recovery nach Fail-Stop
Arbeitsauftrag AA-037 erledigt.'''
Path('/opt/struktur/knowledge-pipeline-aggregator-staging/AA-037.md').write_text(report)
print(hashlib.sha256(report.encode()).hexdigest())