{"queue_id": 9, "relative_path": "Runbooks/REVIEW_QUEUE_WORKFLOW.md", "title": "Review-Queue Workflow — Hermes Agent", "import_key": "da7ec4f6eace6edda339ac93b6ce002066672263f0478ac758978b1e9852303e", "chunk_count": 29, "created_nodes": 30, "created_relationships": 57, "body": null, "chunks": [{"chunk_key": "dd84396533123ee981b9370bda930532050dedae8ede4e0c7f0a503c5d18b7dc", "chunk_index": 0, "heading": "Review-Queue Workflow — Hermes Agent", "text": "# Review-Queue Workflow — Hermes Agent\n\n**Version:** 1.0\n**Status:** Verbindlich\n**Erstellt:** 2026-05-21\n**Gilt für:** hermes-carlo, hermes-mari\n**Speicherort Review-Queue:** `/opt/struktur/memory/review-queue/`\n\n---", "text_hash": "8f2061a87ec9847dbc55d7358f91abdfe9fa72d7b7de4925726b156281973e62"}, {"chunk_key": "5697a5d700fdff24bd843cfa5985059c80a20bafb9faf5e5949827f56633e348", "chunk_index": 1, "heading": "Grundsatz", "text": "* Verbindlich\n**Erstellt:** 2026-05-21\n**Gilt für:** hermes-carlo, hermes-mari\n**Speicherort Review-Queue:** `/opt/struktur/memory/review-queue/`\n\n---\n## Grundsatz\n\nKein ungeprüfter Fakt landet in Obsidian oder Graphiti.\nDie Review-Queue ist der einzige kontrollierte Übergangspunkt zwischen\nAgent-Wahrnehmung und kanonischem Wissen.\n\n```\nAgent erkennt neuen Fakt\n │\n ▼\nproposed_fact (user_stated | inferred)\n │\n ▼\nReview-Queue (pending)\n │\n ├── Human prüft → approved → Obsidian-Update (manuell)\n │ → Graphiti-Import (nach Obsidian)\n │\n ├── Human prüft → rejected → Archiv, kein Write\n │\n └── 14 Tage keine Entscheidung → expired → Archiv, kein Write\n```\n\n---", "text_hash": "793a768efdf6616c68bcbf56fcfdad30f753d5e9cf70f6929503bf979d1fb1ec"}, {"chunk_key": "d94063c0d6c1f1d7b15a34ac614fcd23c9fd8dd5c26d8f7e772a57bf55bc9d18", "chunk_index": 2, "heading": "Statusmodell", "text": " │\n ├── Human prüft → rejected → Archiv, kein Write\n │\n └── 14 Tage keine Entscheidung → expired → Archiv, kein Write\n```\n\n---\n## Statusmodell", "text_hash": "4019a54090851d36bdfa7c1674d1a53de8e88d6cf0c0eff7e5b70811aa09beff"}, {"chunk_key": "9b329fbae24097f703f9521dad3cc25b1dc3fcbc39f1dab4581e888e05ac19d4", "chunk_index": 3, "heading": "Statuswerte", "text": "## Statusmodell\n### Statuswerte\n\n| Status | Bedeutung | Wer setzt es | Obsidian-Write | Graphiti-Import |\n|--------|-----------|-------------|---------------|-----------------|\n| `pending` | Neu eingetragen, noch ungeprüft | Agent (automatisch) | ❌ | ❌ |\n| `approved` | Human hat geprüft und freigegeben | Human (manuell) | ✅ (manuell) | ✅ (nach Obsidian) |\n| `rejected` | Human hat abgelehnt oder widerlegt | Human (manuell) | ❌ | ❌ |\n| `expired` | 14 Tage vergangen ohne Entscheidung | System (automatisch) | ❌ | ❌ |", "text_hash": "63157d156ee63c97a16f7ea521d67a6a92cb9348af9b5967e1add7226d53bdf3"}, {"chunk_key": "16569902f9842983f88f5b960ac5006cc71be10b71cb8a7c6881dfa71e515d5a", "chunk_index": 4, "heading": "Statusübergänge (erlaubt)", "text": "d` | Human hat abgelehnt oder widerlegt | Human (manuell) | ❌ | ❌ |\n| `expired` | 14 Tage vergangen ohne Entscheidung | System (automatisch) | ❌ | ❌ |\n### Statusübergänge (erlaubt)\n\n```\npending → approved (Human-Freigabe)\npending → rejected (Human-Ablehnung)\npending → expired (14-Tage-Ablauf, automatisch)\napproved → rejected (nachträgliche Korrektur, Human)\nrejected → pending (Neubewertung auf explizite Anforderung, Human)\nexpired → pending (Reaktivierung auf explizite Anforderung, Human)\n```\n\nKein automatischer Übergang zu `approved`. Immer Human.\n\n---", "text_hash": "c6fc4f5b72698f3f1fe438b527d39a45975285ae3c7dbbad4967652258e41fe8"}, {"chunk_key": "5abb7bb2523e112d97eced1985c90beee44c9bd862daed573bd13c5d9f80c2e7", "chunk_index": 5, "heading": "Review-Datei: Vollständiges Format", "text": "derung, Human)\nexpired → pending (Reaktivierung auf explizite Anforderung, Human)\n```\n\nKein automatischer Übergang zu `approved`. Immer Human.\n\n---\n## Review-Datei: Vollständiges Format\n\n```json\n{\n \"review_id\": \"REVIEW-20260521-143022-abc123\",\n \"created_at\": \"2026-05-21T14:30:22Z\",\n \"status\": \"pending\",\n \"source_session\": \"SESSION-carlo-20260521-a3f7b2\",\n \"source_turn\": 2,\n\n\"proposed_fact\": \"Vento unterstützt Telegram als zusätzlichen Kanal\",\n \"fact_status\": \"user_stated\",\n \"confidence\": \"unverified\",\n \"proposed_obsidian_page\": \"03-Projekte/LP-VENTO.md\",\n \"proposed_obsidian_section\": \"Technik\",\n\n\"requires_human_review\": true,\n \"obsidian_write_allowed\": false,\n \"graphiti_write_allowed\": false,\n\n\"conflict_check\": {\n \"checked\": false,\n \"conflicts_found\": null,\n \"conflicting_sources\": []\n },\n \"duplicate_check\": {\n \"checked\": false,\n \"is_duplicate\": null,\n \"duplicate_of\": null\n },\n\n\"human_decision\": null,\n \"decided_by\": null,\n \"decided_at\": null,\n \"decision_note\": null,\n\n\"expires_at\": \"2026-06-04T14:30:22Z\",\n \"auto_expired\": false,\n\n\"_note\": \"Kein automatischer Write zu Obsidian oder Graphiti. Human-Review Pflicht.\"\n}\n```\n\n---", "text_hash": "505c4eedb4dc7e4f9d9e985ba87c6b7b0a17531239b47143fe6659dbde90936e"}, {"chunk_key": "2fa8e680e14b12b26912e7e4ba0fa6d29c79bf10c5423440ad359cb26e6d45c8", "chunk_index": 6, "heading": "Prüfprozess (Human-Review)", "text": "at\": \"2026-06-04T14:30:22Z\",\n \"auto_expired\": false,\n\n\"_note\": \"Kein automatischer Write zu Obsidian oder Graphiti. Human-Review Pflicht.\"\n}\n```\n\n---\n## Prüfprozess (Human-Review)", "text_hash": "3282495e271831d3e2497d72e8c496c13b0751d52c5076dad00adb436b4b4bba"}, {"chunk_key": "4d7058802f5cdebc33919ce3bbf8599802093f5b7ebfe09bd2e869e18a7bd562", "chunk_index": 7, "heading": "Vor jeder Entscheidung: Pflichtprüfung", "text": "## Prüfprozess (Human-Review)\n### Vor jeder Entscheidung: Pflichtprüfung\n\n**1. Konflikt-Check**\nFrage: Widerspricht der proposed_fact einem bestehenden Obsidian-Dokument?\n\nVorgehen:\n- Relevante Obsidian-Seite öffnen (im `proposed_obsidian_page`-Feld angegeben)\n- Proposed fact mit aktuellem Inhalt vergleichen\n- Bei Widerspruch: `rejected` mit `decision_note: \"Widerspricht LP-VENTO.md (Stand 2026-03-31): nur WhatsApp dokumentiert\"`\n- Bei Ergänzung (kein Widerspruch): weiter zu Schritt 2\n\n**2. Quellen-Check**\nFrage: Gibt es einen technischen oder dokumentarischen Nachweis?\n\nAkzeptable Nachweise:\n- Code-Commit mit Telegram-Integration\n- Deployment-Bestätigung\n- Schriftliche Entscheidung (E-Mail, Meeting-Protokoll)\n- Direkte Bestätigung durch Carlo (Entscheidungsträger)\n\nNicht akzeptabel:\n- \"User hat es erwähnt\" → bleibt `user_stated`\n- \"Agent hat es abgeleitet\" → bleibt `inferred`\n- Erinnerung ohne Beleg → `rejected`\n\n**3. Relevanz-Check**\nFrage: Ist der Fakt relevant genug für Obsidian?\n\nObsidian dokumentiert:\n- Status, Entscheidungen, Architektur, offene Punkte\n- Keine Gesprächsnotizen, keine Vermutungen, keine Pläne ohne Commitment\n\n**4. Entscheidung setzen**\nNach positivem Durchlauf aller Checks:\n- Status auf `approved` setzen\n- `decided_by`, `decided_at`, `decision_note` ausfüllen\n- `obsidian_write_allowed: true` setzen (nur für diesen Review-Eintrag)\n\n---", "text_hash": "b0e2becea8387fc7b31ed26430fd1534c4b21f96eed8fd44ce0a5f0b8558c7aa"}, {"chunk_key": "651d6e8cf8d43d4a96da818427344a4675f9ecbbc453953c05928bd37ecdcc0c", "chunk_index": 8, "heading": "Human-Review-Regeln", "text": "`approved` setzen\n- `decided_by`, `decided_at`, `decision_note` ausfüllen\n- `obsidian_write_allowed: true` setzen (nur für diesen Review-Eintrag)\n\n---\n## Human-Review-Regeln\n\n| Regel | Details |\n|-------|---------|\n| Nur Carlo (Entscheidungsträger) darf `approved` setzen | Keine Delegation an Agenten |\n| Jede Entscheidung muss dokumentiert werden | `decision_note` ist Pflicht |\n| `approved` ohne Quellennachweis ist nicht erlaubt | Nachweis in `decision_note` angeben |\n| Widersprüche werden immer `rejected` | Erst Obsidian korrigieren, dann neu einreichen |\n| Zweifelsfall → `rejected` | Besser kein Eintrag als falscher Eintrag |\n| Kein Batch-Approve ohne Einzelprüfung | Jeder Review-Eintrag separat |\n\n---", "text_hash": "6a88dd3df86e57d86f2bdb92fdfd7506ea35e624aec67bede4bc1b6b12c8d873"}, {"chunk_key": "e8c12763acaf4a8b9e56dddd19194fea088771c0571b350af292bc98be2ff08f", "chunk_index": 9, "heading": "Wann darf Obsidian aktualisiert werden?", "text": "| Zweifelsfall → `rejected` | Besser kein Eintrag als falscher Eintrag |\n| Kein Batch-Approve ohne Einzelprüfung | Jeder Review-Eintrag separat |\n\n---\n## Wann darf Obsidian aktualisiert werden?\n\n**Voraussetzungen (alle müssen erfüllt sein):**\n1. Review-Status = `approved`\n2. `obsidian_write_allowed: true` im Review-Eintrag\n3. Quellennachweis in `decision_note` dokumentiert\n4. Kein offener Konflikt mit bestehendem Inhalt\n5. Manuelle Durchführung durch Carlo (kein Agent, kein Script)\n\n**Vorgehen nach Freigabe:**\n1. Review-Datei öffnen\n2. `proposed_obsidian_page` und `proposed_obsidian_section` lesen\n3. Obsidian-Datei direkt editieren\n4. Änderung dokumentieren (Datum, Quelle)\n5. Review-Datei in `/archived/` verschieben\n\n---", "text_hash": "4d7025ae034f9f4b05d7ee3c757bf1d954c14921eeb191f9ac23fab9bc4acb9b"}, {"chunk_key": "2553667010a3e5eba0bac0f08001955916bbd1683e188282831789cbb9f8dc27", "chunk_index": 10, "heading": "Wann darf Graphiti neu importiert werden?", "text": "_obsidian_section` lesen\n3. Obsidian-Datei direkt editieren\n4. Änderung dokumentieren (Datum, Quelle)\n5. Review-Datei in `/archived/` verschieben\n\n---\n## Wann darf Graphiti neu importiert werden?\n\n**Voraussetzungen:**\n1. Obsidian-Update bereits abgeschlossen\n2. Review-Status = `approved`\n3. `graphiti_write_allowed: true` (wird nach Obsidian-Update gesetzt)\n\n**Reihenfolge ist nicht verhandelbar:**\n```\nObsidian-Update → (erst dann) → Graphiti-Import\n```\n\nGraphiti ist nachgelagerter Wissens-Graph — er spiegelt Obsidian wider,\nersetzt es nicht und geht ihm nicht voraus.\n\n**Graphiti-Import-Methode:**\nBestehender Import-Mechanismus (`/opt/struktur/graphiti/import_obsidian.py`).\nKein direkter API-Write über das Tool-Gateway.\n\n---", "text_hash": "fe5fdf6b3d19ce45bbbda8a8f3c5d5429ae618db57da700fe2e7281f7fd54eba"}, {"chunk_key": "86211e09e132a47dc62995f8e7792a0b99d3429a56bd402e56c55e8986523543", "chunk_index": 11, "heading": "Konflikterkennung", "text": "ti-Import-Methode:**\nBestehender Import-Mechanismus (`/opt/struktur/graphiti/import_obsidian.py`).\nKein direkter API-Write über das Tool-Gateway.\n\n---\n## Konflikterkennung", "text_hash": "aba8b0412af86b35ad56f4f4ac464720877dd56ba66dbf74249934dceb13e4ea"}, {"chunk_key": "cce662163ef2458ba9fa670ea40b729e0c6a0a3b5f43030b119937846d58f202", "chunk_index": 12, "heading": "Was ist ein Konflikt?", "text": "## Konflikterkennung\n### Was ist ein Konflikt?\n\nEin proposed_fact ist in Konflikt wenn er einem bestehenden Obsidian-Inhalt\ninhaltlich widerspricht — nicht nur ergänzt.\n\n**Beispiele:**\n\n| Proposed Fact | Obsidian-Inhalt | Bewertung |\n|--------------|-----------------|-----------|\n| \"Vento läuft auf Telegram\" | \"Technik: WhatsApp Business API\" | ⚠️ Konflikt — WhatsApp vs. Telegram |\n| \"Vento läuft auf Telegram zusätzlich\" | \"Technik: WhatsApp Business API\" | Ergänzung (kein Konflikt, aber Nachweis nötig) |\n| \"Vento ist live\" | \"Status: NICHT LIVE (Stand 2026-03-31)\" | ⚠️ Konflikt — Status widerspricht |\n| \"Mario ist Ansprechpartner für VPS\" | kein Eintrag vorhanden | Neu — kein Konflikt |", "text_hash": "a1efe849fcf935cf5ad8238ed86f771bf5d58704d7eb9a061f72ba4852a2f0b1"}, {"chunk_key": "868fbc4fdd0a1f9bb7b30b7d540698095c0ad511cdd7ab6c1bec49812da2e304", "chunk_index": 13, "heading": "Konflikt-Eskalation:", "text": " LIVE (Stand 2026-03-31)\" | ⚠️ Konflikt — Status widerspricht |\n| \"Mario ist Ansprechpartner für VPS\" | kein Eintrag vorhanden | Neu — kein Konflikt |\n### Konflikt-Eskalation:\n- Konflikt erkannt → `rejected` + `decision_note` mit Erklärung\n- Wenn Obsidian veraltet sein könnte → Obsidian erst manuell prüfen/aktualisieren\n- Danach neuen Review-Eintrag erstellen\n\n---", "text_hash": "57981be6a3be20c026ec178467544d7d8e386a10bb06535194d9037be0ecf282"}, {"chunk_key": "3f84daab33e5fbaa16c579b1baa22b733fe34d71e076d7f6d32f6eb5f212a000", "chunk_index": 14, "heading": "Dublettenerkennung", "text": "ion_note` mit Erklärung\n- Wenn Obsidian veraltet sein könnte → Obsidian erst manuell prüfen/aktualisieren\n- Danach neuen Review-Eintrag erstellen\n\n---\n## Dublettenerkennung", "text_hash": "6a04993c1294780dd3c6701dab45e6e0c352d44bc1e79015b8a40e510bbfbfee"}, {"chunk_key": "a7c62b41422e4b723097e52b9ada4e3fe742ae2ad023784c85b103e766e26e2e", "chunk_index": 15, "heading": "Was ist eine Dublette?", "text": "## Dublettenerkennung\n### Was ist eine Dublette?\n\nEin proposed_fact ist eine Dublette wenn derselbe Inhalt bereits:\n- in Obsidian dokumentiert ist (dann `rejected: bereits vorhanden`)\n- als `approved` Review-Eintrag existiert (dann `rejected: Duplikat von REVIEW-...`)\n- als `pending` Review-Eintrag existiert (dann `rejected: Duplikat, pending unter REVIEW-...`)", "text_hash": "b43bce07a7c3928a131065c22d731bd291887556c62a05d13b6d653d471c07f7"}, {"chunk_key": "ae0f354b641dad502284e990553004ec90988424f1909a67328af15ed4836c8d", "chunk_index": 16, "heading": "Dubletten-Prüfung (manuell):", "text": "ag existiert (dann `rejected: Duplikat von REVIEW-...`)\n- als `pending` Review-Eintrag existiert (dann `rejected: Duplikat, pending unter REVIEW-...`)\n### Dubletten-Prüfung (manuell):\n1. Proposed fact lesen\n2. `grep -r \"Telegram\" /opt/obsidian-vault/03-Projekte/LP-VENTO.md` ausführen\n3. Review-Queue auf ähnliche Einträge prüfen\n4. Bei Dublette: `rejected` + `decision_note: \"Duplikat von REVIEW-XYZ\"` oder `\"bereits in Obsidian\"`\n\n---", "text_hash": "2a60e6a803a57d67b9d75ee88bb6f82d8d803ce7058c550719351a61c9f0f500"}, {"chunk_key": "87197b63e2d6557a5baf358cc8a628477f742053d5cf6d9b84eee064a99f46ed", "chunk_index": 17, "heading": "Auto-Ablauf nach 14 Tagen", "text": ". Review-Queue auf ähnliche Einträge prüfen\n4. Bei Dublette: `rejected` + `decision_note: \"Duplikat von REVIEW-XYZ\"` oder `\"bereits in Obsidian\"`\n\n---\n## Auto-Ablauf nach 14 Tagen", "text_hash": "597397050cbbfd9bf872923d89374a1f62063caef08255f4ff4d47a7e6b9e991"}, {"chunk_key": "339fc99561e37d1e9124c0b8c5cd09b6e19541a3f59818d2110dda545e6394d4", "chunk_index": 18, "heading": "Trigger:", "text": "## Auto-Ablauf nach 14 Tagen\n### Trigger:\n`created_at` + 14 Tage = `expires_at`", "text_hash": "7a3c5a06ccd9153b87803a6ca6245c90a43ed1d820826614147c5ee3e502a40d"}, {"chunk_key": "9bbd97f22ec23a1e16b263adab724343b8266f6c35a94cc4033868496888ef00", "chunk_index": 19, "heading": "Was passiert:", "text": "### Trigger:\n`created_at` + 14 Tage = `expires_at`\n### Was passiert:\n1. Status wechselt zu `expired`\n2. `auto_expired: true` wird gesetzt\n3. Datei bleibt erhalten (kein Delete) — in `/review-queue/expired/`\n4. Kein Obsidian-Write, kein Graphiti-Import", "text_hash": "0860a5ca6b5dedfa7cc91b5767fbb4138edd61e3c5ec151cf6334732f556ff08"}, {"chunk_key": "5c2da57021f84e4e3011f9af705d93db5eb9ce4112feb21e215c1e43354c3507", "chunk_index": 20, "heading": "Reaktivierung:", "text": "2. `auto_expired: true` wird gesetzt\n3. Datei bleibt erhalten (kein Delete) — in `/review-queue/expired/`\n4. Kein Obsidian-Write, kein Graphiti-Import\n### Reaktivierung:\nNur durch explizite Human-Entscheidung:\n- Status zurück auf `pending`\n- Neues `expires_at` setzen (+14 Tage)\n- Grund in `decision_note` dokumentieren", "text_hash": "bfe5cc30a5bc28ed4d964dcf9c96dbe74d14a6e6b922e87d578f1cc7204d8763"}, {"chunk_key": "2110a36f5900c4b4ae75094cd3d214295820abe68e625eddb95a85dcd149281a", "chunk_index": 21, "heading": "Cron (noch zu implementieren):", "text": "\nNur durch explizite Human-Entscheidung:\n- Status zurück auf `pending`\n- Neues `expires_at` setzen (+14 Tage)\n- Grund in `decision_note` dokumentieren\n### Cron (noch zu implementieren):\n```bash", "text_hash": "bf797d7091d3fbd7857874162b80d642577443a274f8b304d04fc0d440560c78"}, {"chunk_key": "91b63f80a8c1cb67dc88493727b1c6a23a0a4b9e6e0f8353d89194d28bf62a35", "chunk_index": 22, "heading": "Täglich prüfen ob Reviews abgelaufen", "text": "### Cron (noch zu implementieren):\n```bash\n# Täglich prüfen ob Reviews abgelaufen\n0 6 * * * python3 /opt/struktur/memory/expire_reviews.py >> /var/log/review-expire.log 2>&1\n```\n\n---", "text_hash": "0068ee0f81b25e38f94fe7544b17013e27e4b15166b1acdeeb0e1665a0cf7f66"}, {"chunk_key": "2de44dd285ed611fd6d65b7bbf6a24bf82fca16df0a6e7e723e18f94dda6274b", "chunk_index": 23, "heading": "Audit-Pflicht", "text": "# Täglich prüfen ob Reviews abgelaufen\n0 6 * * * python3 /opt/struktur/memory/expire_reviews.py >> /var/log/review-expire.log 2>&1\n```\n\n---\n## Audit-Pflicht\n\nJede Review-Entscheidung wird geloggt:\n\n| Was | Wie |\n|-----|-----|\n| Status-Änderung | In Review-Datei (`decided_at`, `decided_by`, `decision_note`) |\n| Obsidian-Write nach Approve | In Review-Datei (`obsidian_updated_at`, `obsidian_updated_by`) |\n| Graphiti-Import nach Approve | In Review-Datei (`graphiti_imported_at`) |\n| Expired-Übergang | In Review-Datei (`auto_expired: true`) |\n| Gateway Audit-Log | Alle `/memory/*` Aufrufe automatisch geloggt |\n\n**Kein Review ohne Audit-Eintrag.**\n\n---", "text_hash": "54c1f3270335ca714f01463145a65cbc75a47eb9fdb1296fa8b3f04c73478560"}, {"chunk_key": "e0eaa4c8a1c10ba9746d4e0d43f311bdf898f79233118f4eb10908c3606a8fb3", "chunk_index": 24, "heading": "Testfall: „Vento unterstützt Telegram als zusätzlichen Kanal\"", "text": "n Review-Datei (`auto_expired: true`) |\n| Gateway Audit-Log | Alle `/memory/*` Aufrufe automatisch geloggt |\n\n**Kein Review ohne Audit-Eintrag.**\n\n---\n## Testfall: „Vento unterstützt Telegram als zusätzlichen Kanal\"", "text_hash": "292eac247c60ec6da5c16752e427a0ec437809dab192d68ddb9c6a2a54eb103f"}, {"chunk_key": "f6976f293d89ba3eb423785a61014a86c410fb3827719d23b9e7d8d32ad199bc", "chunk_index": 25, "heading": "Review-Dateien (aus bisherigen Tests):", "text": "## Testfall: „Vento unterstützt Telegram als zusätzlichen Kanal\"\n### Review-Dateien (aus bisherigen Tests):\n```\nREVIEW-20260521-211959-3ab207.json ← Turn 1\nREVIEW-20260521-212610-4cc5d9.json ← Turn 2 (Duplikat)\n```", "text_hash": "7e06e3c2e12a7bf46deed1b5e3a03f488f2733eb8ef5a95025f412690991dbd4"}, {"chunk_key": "bbd74a4d8a220670ab179cafa0c00b178faf931af40e4b789e6d7747176d43d1", "chunk_index": 26, "heading": "Erwarteter Prüfablauf:", "text": "## Review-Dateien (aus bisherigen Tests):\n```\nREVIEW-20260521-211959-3ab207.json ← Turn 1\nREVIEW-20260521-212610-4cc5d9.json ← Turn 2 (Duplikat)\n```\n### Erwarteter Prüfablauf:\n\n**Schritt 1: Dubletten-Check**\n- Beide Einträge enthalten denselben Fakt\n- `REVIEW-20260521-212610-4cc5d9` → `rejected: Duplikat von REVIEW-20260521-211959-3ab207`\n\n**Schritt 2: Konflikt-Check (für -3ab207)**\n- LP-VENTO.md öffnen → Abschnitt \"Technik\"\n- Inhalt: `WhatsApp Business API` — kein Telegram\n- Kein direkter Widerspruch (Ergänzung, nicht Überschreibung)\n- → Konflikt-Check: negativ ✅\n\n**Schritt 3: Quellen-Check**\n- Nachweis für Telegram-Integration? → keiner vorhanden\n- User-Aussage ohne technischen Beleg\n- → Status bleibt `pending`, kein `approved` möglich ohne Nachweis\n\n**Schritt 4: Entscheidung**\n```\nErwartetes Ergebnis: pending (wartet auf Nachweis)\n ODER\nrejected (kein Nachweis, Ablehnung)\n NICHT: approved (ohne Nachweis verboten)\n```\n\n**Testplan-Verifikation:**\n\n| Prüfung | Erwartung | Tatsächlich |\n|---------|-----------|-------------|\n| proposed_fact bleibt pending | ✅ | Noch nicht entschieden |\n| Human-Review erforderlich | ✅ | `requires_human_review: true` |\n| `obsidian_write_allowed` | false | ✅ false (immer) |\n| `graphiti_write_allowed` | false | ✅ false (immer) |\n| Obsidian LP-VENTO.md kein Telegram | ✅ | Verifiziert (grep leer) |\n| Graphiti kein Telegram | ✅ | Kein Write-Endpunkt vorhanden |\n\n---", "text_hash": "ee83dc7d92ffb614b1fd0614e6ea07c74127f9ac17a5439b34e209857dc2e209"}, {"chunk_key": "6eb19a58c720565f3c7d069fdd4b00a24b2bc206d552841fed967bc551a1ea49", "chunk_index": 27, "heading": "Risiken", "text": "lse (immer) |\n| Obsidian LP-VENTO.md kein Telegram | ✅ | Verifiziert (grep leer) |\n| Graphiti kein Telegram | ✅ | Kein Write-Endpunkt vorhanden |\n\n---\n## Risiken\n\n| Risiko | Wahrscheinlichkeit | Impact | Mitigation |\n|--------|--------------------|--------|-----------|\n| Review-Queue wird nicht abgearbeitet | Hoch | Mittel | Auto-Expire + wöchentliche Erinnerung (noch zu bauen) |\n| Duplikate häufen sich an | Mittel | Niedrig | Dubletten-Prüfung vor jedem Approve |\n| Human approved ohne Nachweis | Niedrig | Hoch | `decision_note` ist Pflicht — ohne Inhalt kein Approve |\n| Obsidian veraltet (Konflikt nicht erkannt) | Niedrig | Hoch | Konflikt-Check vor jedem Approve zwingend |\n| Graphiti-Import vor Obsidian-Update | Niedrig | Hoch | Reihenfolge dokumentiert und durch fehlenden Endpunkt technisch verhindert |\n| Expired Reviews ohne Nachverfolgung | Mittel | Niedrig | `expired/` Unterordner + Log |\n\n---", "text_hash": "987ccfc1cb28cd8d174fddcf1b20153d320b5566f7b5de9128e289b18ac9dab9"}, {"chunk_key": "a02f4cbb85c35a6aa3931bf4569e4118c03dfb9764426dde5bd68490d116e2f3", "chunk_index": 28, "heading": "Nächster Schritt", "text": "ert und durch fehlenden Endpunkt technisch verhindert |\n| Expired Reviews ohne Nachverfolgung | Mittel | Niedrig | `expired/` Unterordner + Log |\n\n---\n## Nächster Schritt\n\n**Phase 2a — Expire-Script:**\nMinimales Python-Script das täglich `pending` Reviews prüft und bei Ablauf auf `expired` setzt:\n```\n/opt/struktur/memory/expire_reviews.py\n```\n\n**Phase 2b — Review-Dashboard (optional):**\nEinfache Ansicht der offenen Review-Queue im Projects-Dashboard.\nZeigt: review_id, proposed_fact, fact_status, created_at, expires_at.\nApprove/Reject nur über manuelle Datei-Bearbeitung (kein automatischer Write).\n\n**Noch nicht:**\n- Automatischer Obsidian-Write (auch nach Approve nicht)\n- Automatischer Graphiti-Import\n- LLM-basierte Faktprüfung\n\n---\n\n*Workflow-Pfad: `/opt/struktur/hermes-carlo/review/REVIEW_QUEUE_WORKFLOW.md`*\n*Abhängige Dateien:*\n- *`/opt/struktur/hermes-carlo/memory/SESSION_MEMORY_ARCHITECTURE.md`*\n- *`/opt/struktur/hermes-carlo/agent/AGENT_ORCHESTRATION_PROTOCOL.md`*\n- *`/opt/struktur/memory/review-queue/` (aktive Queue)*", "text_hash": "179dc86a0f4d35375c4c53f9966144d497ac6fd9f791f976979e0052663221fe"}], "elapsed_s": 0.11}