Explorer
/proc/560/root/tmp/write_phase1_report.py
← Zurück ↓ Download
import json,datetime,hashlib
from pathlib import Path
d=json.load(open('/tmp/phase1_final_data.json')); L=[]
def a(x=''):L.append(x)
def v(x):return str(x if x is not None else '—').replace('|','\\|').replace('\n',' ')
backup=Path('/home/hermes/Wissenspipeline-Repair-Phase-1-Backup-20260902T231732Z'); bdb=backup/'knowledge.db'
a('# Wissenspipeline – Repair-Phase 1 – Abschlussbericht');a();a(f"**Finale Erhebung:** `{d['generated_at']}` UTC  ");a('**Arbeitsmodus:** kontrollierte Queue-Recovery; FK-Rekonstruktionsprüfung ausschließlich read-only.');a()
a('## 1. Executive Summary');a();a('- Repair-Phase 1 wurde auf dem vorhandenen Backup-/Recovery-Stand fortgesetzt und vollständig dokumentiert.');a('- Queue 1092 und 1268 wurden bereits kontrolliert recovered und anschließend ausschließlich über den regulären AA-049/AA-043-Pfad verarbeitet. Beide sind final `done`; es existiert je genau eine exakte Graphiti-Identity.');a('- Queue 347 wurde nicht blind requeued: Der aktive AA-049-Selector verlangt `video_id IS NOT NULL`; Queue 347 hat `video_id=NULL`, ist ein `Pilot/README.md`-Legacyfall und bleibt bewusst offen.');a('- Final offen: 13 Zeilen, 9 `queued`, 4 `retry_wait`, 0 `processing`.');a('- FK-Verstöße bleiben unverändert bei 485; keine Parent-/Child-Daten wurden erzeugt, gelöscht oder umgeschrieben.');a()
a('| Prüffeld | Finaler Nachweis |');a('|---|---|');a(f"| DB-Größe | {d['db']['size']} Byte |");a(f"| `integrity_check` | `{d['db']['integrity']}` |");a(f"| `foreign_key_check` | {d['db']['fk']} |");a(f"| FK-Modus der Audit-Verbindung | `{d['db']['fk_mode']}` |");a('| Queue | 1.278 done / 9 queued / 4 retry_wait / 0 processing |');a('| Graphiti | Health OK, Search HTTP 200, 3.194 Episoden |');a()
a('## 2. Backup-Nachweis');a();a(f'- Backup-Pfad: `{backup}`');a('- Zeitpunkt: 2026-09-02T23:17:32Z laut Backup-Verzeichnis/Manifest.');a(f'- Konsistente SQLite-Online-Kopie: `{bdb}`, {bdb.stat().st_size} Byte, SHA-256 `{hashlib.sha256(bdb.read_bytes()).hexdigest()}`.');a('- Enthalten sind DB, WAL-/SHM-Zustandsbeschreibung, Queue-/Registry-/Source-Version-Snapshots, FK-Snapshot, relevante systemd-/Worker-/Writer-Dateien und AA-049-Eventlog.');a('- Das Backup wurde vor allen Queue-Statusänderungen erstellt und ist lesbar.');a()
a('## 3. Ausgangszustand');a();a('- Preflight 2026-09-02T23:17:07Z: 1.276 done, 2 processing, 9 queued, 4 retry_wait; 485 FK-Verstöße; `integrity_check=ok`; WAL; FK-Default 0.');a('- 1092/1268: verwaiste `processing`-Claims mit abgelaufener Lease, `aa043-single-writer`, `UNKNOWN_REMOTE_OUTCOME`, HTTP 500, keine exakte Episode.');a('- 347: `retry_wait`, 6 Versuche, HTTP 500, `next_attempt_at=2026-07-19T10:59:26Z`, `video_id=NULL`.');a()
a('## 4. Queue 1092');a();
for x in d['targets']:
 if x['id']==1092:
  a('| Feld | Wert |');a('|---|---|')
  for k in ['id','source_type','video_id','obsidian_path','graphiti_status','graphiti_episode_id','remote_outcome','last_error_class','last_http_status','lock_owner','lease_expires_at','attempt_count','graphiti_attempts','updated_at','completed_at','import_identity','source_version_id']:a(f"| `{k}` | `{v(x.get(k))}` |")
a();a('- Vorher wurden Datei-SHA-256 und Frontmatter-YouTube-ID gegen Queue-Daten geprüft; keine exakte Episode und kein aktiver Writer-Claim waren vorhanden.');a('- Guarded Recovery: `processing → lease_expired → queued`, Owner/Lease freigegeben, Identity und Attempts erhalten.');a('- Erster regulärer Versuch endete mit Provider HTTP 429 und `retry_wait`; kein Graphiti-Erfolg wurde behauptet.');a('- Späterer regulärer AA-049-Lauf: `done`, `WRITE_CONFIRMED`, Episode `fef7212b-a9b6-4a77-984c-ce5f292a9604`, abgeschlossen 2026-09-03T00:36:29Z.');a()
a('## 5. Queue 1268');a();
for x in d['targets']:
 if x['id']==1268:
  a('| Feld | Wert |');a('|---|---|')
  for k in ['id','source_type','video_id','obsidian_path','graphiti_status','graphiti_episode_id','remote_outcome','last_error_class','last_http_status','lock_owner','lease_expires_at','attempt_count','graphiti_attempts','updated_at','completed_at','import_identity','source_version_id']:a(f"| `{k}` | `{v(x.get(k))}` |")
a();a('- Guarded Recovery: `processing → lease_expired → queued`, dann regulärer AA-049-Writer.');a('- Graphiti HTTP 200; Episode `5ca55a1b-c326-4b25-9151-e6c526fbbf5e`; final `done`.');a('- Das vollständige Graphiti-Inventar enthielt für die Import-Identity exakt einen Treffer mit dieser UUID.');a()
a('## 6. Queue 347');a();
for x in d['targets']:
 if x['id']==347:
  a('| Feld | Wert |');a('|---|---|')
  for k in ['id','source_type','video_id','obsidian_path','graphiti_status','next_attempt_at','attempt_count','graphiti_attempts','last_error_class','last_http_status','graphiti_episode_id','source_version_id','import_identity','updated_at']:a(f"| `{k}` | `{v(x.get(k))}` |")
a();a('- Der produktive AA-049-Worker filtert auf `source_type=\'youtube\'`, `video_id IS NOT NULL`, Status `queued`/`retry_wait` und fällige Retry-Zeit.');a('- Queue 347 ist zwar fällig und hat den richtigen `source_type`, wird aber wegen `video_id=NULL` nicht ausgewählt. Das erklärt die wiederholten `NO_DUE_YOUTUBE`-Events.');a('- Der Pfad `Pilot/README.md` ist ein erlaubter Scope-Root, aber kein belastbarer YouTube-Artifact: kein YouTube-Frontmatter, keine `video_id`. Source-Version 347 ist `OBSERVED`/`UNRECONCILED`; exakte Episode nicht im Inventory.');a('- Eine sichere Wiederverarbeitung ist nicht möglich, weil die reale YouTube-Identität fehlt. Queue 347 wurde nicht verändert.');a()
a('## 7. Alle verbleibenden offenen Queue-Einträge');a();a('| ID | Status | Ursache offen | fällig? | Retry/Lease | Episode vorhanden | nächster zulässiger Zustand |');a('|---:|---|---|---|---|---|---|')
for x in d['open']:
 if x['id']==347: cause='Legacy/Pilot: `video_id=NULL`, Selector schließt aus'; nxt='Review/Identität ergänzen; kein Auto-Requeue'
 elif x['graphiti_status']=='retry_wait': cause='alter Retry-/Timeout-/HTTP-500-Fall ohne gültigen Claim'; nxt='Einzelprüfung/Entscheidung'
 else: cause='`obsidian-general`, separater allgemeiner Obsidian-Pfad'; nxt='generischer Import/Review'
 due='ja' if x.get('next_attempt_at') else ('ja, ohne Termin' if x['graphiti_status']=='queued' else 'überfällig')
 a(f"| {x['id']} | `{x['graphiti_status']}` | {cause} | {due} | {v(x.get('next_attempt_at'))} / frei | nein/nicht nachgewiesen | {nxt} |")
a();a('Es bleibt kein ungeklärtes `processing` bestehen. Die 9 allgemeinen Obsidian-Zeilen werden nicht vom YouTube-AA-049-Selector verarbeitet; das ist ein bewusst getrennter Pfad, kein hängender YouTube-Claim.');a()
a('## 8. Recovery-Logik');a();a('- Verwaiste Claims entstehen durch Writer-/POST-/Timeout-Abbruch nach lokalem Claim. Der Writer hält danach `processing`/`UNKNOWN_REMOTE_OUTCOME`, um keinen falschen Erfolg zu melden.');a('- Lease-Expiry wird als Zustand unterstützt, aber der produktive AA-049-Selector nimmt `lease_expired` nicht direkt auf. Deshalb war die kontrollierte zweistufige Recovery notwendig: guarded auf `lease_expired`, danach scheduler-kompatibel auf `queued`.');a('- Atomisches Claiming, Owner, Token, Heartbeat, 20-Minuten-Lease, Exact-Identity-Prüfung, Hash-/Unique-Schutz und bestätigtes Success-State-Update funktionieren.');a('- Lücke: kein nachgewiesener automatisch laufender Lease-Recovery-Worker; ferner bleiben Attempt-Zähler bei Unknown-Outcome-Fällen 0.');a('- Sicherer kleiner Fix für die Zukunft: reviewpflichtiger Lease-Recovery-Schritt mit Exact-Identity-Check, der nur abgelaufene Claims behandelt und niemals direkt eine Episode erzeugt. Vor produktiver Implementierung sind Testkopie, Backup und Rollback erforderlich.');a()
a('## 9. Regressionstests');a();a('| Test | Ergebnis | Nachweis |');a('|---|---|---|');a('| `PRAGMA integrity_check` | PASS | `ok` |');a('| `PRAGMA foreign_key_check` | unverändert 485 | read-only |');a('| Queue-Verteilung | PASS | 1.278 done / 9 queued / 4 retry_wait / 0 processing |');a('| keine Processing-Leichen | PASS | 0 Zeilen |');a('| AA-049-Timer | PASS | active/waiting, periodische `NO_DUE_YOUTUBE`-Events |');a('| Graphiti Health | PASS | `ready=true`, `neo4j_ready=true`, 0 aktive/stale Requests |');a('| Graphiti Search | PASS | HTTP 200, 3 Facts für `Obsidian` |');a('| Queue 1092 | PASS | genau eine exakte Episode, UUID `fef7212b-a9b6-4a77-984c-ce5f292a9604` |');a('| Queue 1268 | PASS | genau eine exakte Episode, UUID `5ca55a1b-c326-4b25-9151-e6c526fbbf5e` |');a('| Duplicate Write recovered IDs | PASS | je ein exakter Inventory-Treffer |');a();a('Graphiti-Inventar: **3.194 Episoden**. Die Erhöhung gegenüber 3.192 entspricht den beiden bestätigten Writes.');a()
a('## 10. FK-Rekonstruktionsmatrix');a();a('- 485 Verstöße: 279 Chapters und 206 Video Links.');a('- 27 unterschiedliche fehlende Parent-IDs wurden gegen aktuelle Auxiliary-Tabellen, Registry, Rohdaten, Segmente, E2E-Jobs, Queue und 30 geöffnete historische Knowledge-DB-Snapshots geprüft.');a('- Kein Parent konnte dabei eindeutig mit einer YouTube-ID, URL, Source-Version oder vollständigen Video-Masterdaten rekonstruiert werden.');a();a('| fehlender Parent | Chapters | Video Links | Identität rekonstruierbar | Nachweisquelle | sicher wiederherstellbar |');a('|---:|---:|---:|---|---|---|')
for x in d['parents']:a(f"| {x['parent']} | {x['chapters']} | {x['video_links']} | nein | aktuelle Registry {x['registry']}, Raw {x['raw']}, Segmente {x['segments']}, E2E {x['jobs']}, Queue {x['queue']}; historische Snapshots ohne Treffer | nein |")
a();a(f"**Summe:** {len(d['parents'])} fehlende Parents, {sum(x['chapters'] for x in d['parents'])} Chapters, {sum(x['video_links'] for x in d['parents'])} Video Links.");a()
a('## 11. FK-Ursachenklassifikation');a();a('| Klasse | Anzahl sicher zugeordnet | Technischer Befund |');a('|---|---:|---|');a('| versehentlich gelöschter Parent | 0 | kein Löschereignis/Backup-Treffer |');a('| Legacy-ID | 0 | möglich, aber nicht beweisbar |');a('| Import-/Migrationsfehler | 0 | keine konkrete Timeline/Zuordnung beweisbar |');a('| nie korrekt vorhandener Parent | 0 | nicht beweisbar |');a('| veralteter Child-Datensatz | 0 | keine fachliche Entscheidung nachgewiesen |');a('| Ursache nicht beweisbar | **485** | Parent fehlt; keine Identität rekonstruierbar |');a();a('Die einzige belastbare Einstufung ist daher Klasse F: Ursache nicht beweisbar. Keine Dummy-Parents und keine Child-Löschungen.');a()
a('## 12. Spätere FK-Reparatur und Enforcement');a();a('- Nur reale Parent-Identität aus autoritativer Source-Version darf später wiederhergestellt werden.');a('- Alternativ Orphans explizit als Legacy-/Quarantänestatus dokumentieren; kein grüner Check durch künstliche Daten.');a('- Vor FK-ON: alle `sqlite3.connect`-Entry-Points vereinheitlichen; `e2e_worker.py:22` und `db.py:35` setzen bereits FK-ON, direkte App-/Legacy-Pfade sind noch nicht global garantiert.');a('- Testkopie mit FK-ON, realen Write-Sequenzen, erwarteten `IntegrityError`s, Backup und Rollback; danach schrittweise Dienstaktivierung. In dieser Phase nicht global aktiviert.');a()
a('## 13. Writer-Ownership');a();a('| Bereich | Finaler Befund |');a('|---|---|');a('| YouTube → Graphiti | AA-049/AA-043 war der einzige aktive produktive Queue-Writer für 1092/1268. |');a('| Knowledge DB | Mehrere direkte Writer bleiben real aktiv: YouTube-FastAPI, E2E-Worker, Queue-Enqueuer sowie Registry-/Reconciliation-/Legacy-Code. |');a('| Obsidian → Graphiti | unabhängiger generischer `obsidian-graphiti-import.timer` aktiv/waiting. |');a('| Legacy | schreibfähige Dateien vorhanden; für die meisten kein aktueller Prozessnachweis. |');a('| nächster Vereinheitlichungsschritt | generischer Obsidian-Timer und `Automatisch`-Projektionen unter Source/Derived-/Identity-Gates bringen. |');a()
a('## 14. Obsidian-Rückkopplung');a();a('- E2E schreibt `/opt/obsidian-vault/YouTube-Research/Automatisch/*.md`; der rekursive Scanner kann diese Dateien grundsätzlich wieder in die Queue übernehmen.');a('- Pfad+Datei-Hash und Import-Identity schützen bytegleiche Reimports.');a('- Bei neu erzeugten oder veränderten Projektionen bleibt semantische Doppelzählung möglich.');a('- Minimaler späterer Schutz: `SOURCE`/`DERIVED` kennzeichnen, generierte Dateien nicht als unabhängige Evidence zählen, Exact-Identity vor POST, Review bei Hash-/Versionswechsel. Scope Policy wurde nicht geändert.');a()
a('## 15. Tatsächlich vorgenommene Änderungen');a();a('- Backup vor Änderungen erstellt.');a('- Queue 1092 und 1268 guarded recovered und über regulären AA-049/AA-043-Pfad verarbeitet.');a('- Queue 1268 erfolgreich auf `done` gesetzt; Queue 1092 final ebenfalls `done` nach zwischenzeitlichem Provider-429/Retry.');a('- Recovery-Ereignisse im AA-049-Eventlog protokolliert.');a('- Keine FK-Daten, Schemaelemente, Parent-/Child-Datensätze, Scope Policy oder Writer-Konfiguration verändert.');a('- Queue 347 und die übrigen offenen Zeilen nicht blind requeued.');a()
a('## 16. Rollback');a();a(f'- Ausgangspunkt ist das Backup `{backup}`.');a('- Queue-Rollback ist technisch über Snapshot möglich, darf aber wegen der inzwischen bestätigten Graphiti-Episoden nicht isoliert und blind erfolgen. Queue- und Graph-Inventory müssen gemeinsam geprüft werden.');a('- FK-Rollback ist nicht erforderlich, weil keine FK-Daten verändert wurden.');a()
a('## 17. Verbleibende Punkte');a();a('### KRITISCH');a('- 485 FK-Verstöße ohne rekonstruierbaren Parent.');a('- Kein automatischer Lease-Recovery-Schritt nachgewiesen.');a();a('### NORMAL');a('- Queue 347 und drei weitere alte YouTube-README-Retry-Zeilen bleiben wegen fehlender `video_id` offen.');a('- 9 allgemeine Obsidian-Zeilen warten im separaten Pfad.');a('- Source/Derived-Rückkopplung und globale Writer-Ownership noch nicht vollständig erzwungen.');a();a('### OPTIONAL');a('- regelmäßige Entity-/Relationship-Metrik und vollständiger Disaster-Restore-Test.');a()
a('## 18. Entscheidungen für Carlo / Repair-Phase 2');a();a('1. Umgang mit den 485 Orphans: Rekonstruktion, Quarantäne oder dauerhafte Legacy-Dokumentation.');a('2. Freigabe eines reviewpflichtigen automatischen Lease-Recovery-Schritts.');a('3. Behandlung von Queue 347/759/849/859.');a('4. Verbindliche Source-/Derived-Regel für `Automatisch` und den generischen Obsidian-Timer.');a('5. schrittweise FK-ON-Aktivierung und vollständiger Restore-Test.');a()
a('## 19. Nachweisquellen');a();a('- Live-DB: `/opt/struktur/youtube-research/knowledge.db`');a(f'- Backup: `{backup}`');a('- AA-049-Events: `/opt/struktur/reports/aa049-worker-events.jsonl`');a('- Queue-Worker: `/opt/struktur/obsidian-graphiti-import/aa049_queue_worker.py`');a('- Single Writer: `/opt/struktur/obsidian-graphiti-import/aa043/aa043_single_writer.py`');a('- E2E: `/opt/struktur/youtube-research/e2e_worker.py`');a('- DB-/App-Code: `/opt/struktur/youtube-research/db.py`, `/opt/struktur/youtube-research/app.py`');a('- Queue-Enqueuer: `/opt/struktur/graphiti/youtube_graphiti_queue.py`');a('- Graphiti: `/opt/struktur/graphiti/service/main.py`; `/health`, `/search`, `/episodes/inventory`');a('- Scope: `/opt/struktur/graphiti/scope_policy_checker.py`');a();a('Arbeitsauftrag Wissenspipeline-Repair-Phase-1 erledigt.')
Path('/home/hermes/Wissenspipeline-Repair-Phase-1.md').write_text('\n'.join(L)+'\n',encoding='utf8'); print(json.dumps({'path':'/home/hermes/Wissenspipeline-Repair-Phase-1.md','lines':len(L),'bytes':Path('/home/hermes/Wissenspipeline-Repair-Phase-1.md').stat().st_size,'last_line':L[-1]},ensure_ascii=False))