Explorer
/proc/122/root/tmp/fast-one-11.json
← Zurück ↓ Download
{"queue_id": 11, "relative_path": "Runbooks/REVIEW_DECISION_PROTOCOL.md", "title": "Review Decision Protocol — Hermes Agent", "import_key": "fdcd82302a03d8ebe13e6c9b9d47d9aee4c57d5ec581b5df9b471eb9b449038d", "chunk_count": 29, "created_nodes": 30, "created_relationships": 57, "body": null, "chunks": [{"chunk_key": "dd4338a37e1cf4699a96f379c82007a22bd4ea512e6b9934c5327211c1c56297", "chunk_index": 0, "heading": "Review Decision Protocol — Hermes Agent", "text": "# Review Decision Protocol — Hermes Agent\n\n**Version:** 1.0\n**Status:** Verbindlich\n**Erstellt:** 2026-05-21\n**Gilt für:** hermes-carlo, hermes-mari\n**Entscheidungsberechtigt:** Carlo (Entscheidungsträger) — keine Delegation\n\n---", "text_hash": "c471425a74688dbfc68d9060c7f6801ae32879aaee810468f11e9575eb46976e"}, {"chunk_key": "88262aac0678a7d7844037ebdb3ba0f9f7973b7d4fc01e6570ea9d6c193bcaeb", "chunk_index": 1, "heading": "Grundsatz", "text": "lich\n**Erstellt:** 2026-05-21\n**Gilt für:** hermes-carlo, hermes-mari\n**Entscheidungsberechtigt:** Carlo (Entscheidungsträger) — keine Delegation\n\n---\n## Grundsatz\n\nEine Review-Entscheidung ist ein manueller, bewusster Akt.\nKein Script, kein Agent, kein automatischer Prozess darf `approved` setzen.\nJede Entscheidung ist dauerhaft und wird auditiert.\n\n---", "text_hash": "b967a0dce4e17e63ff0b9544c4f540a5547aac86611069385a7fe0c4964a12d7"}, {"chunk_key": "1b8f03b1273369448111c1052a3141401cda74bd0d47a47d33c49703b11a27c1", "chunk_index": 2, "heading": "Entscheidungsberechtigte", "text": "r, bewusster Akt.\nKein Script, kein Agent, kein automatischer Prozess darf `approved` setzen.\nJede Entscheidung ist dauerhaft und wird auditiert.\n\n---\n## Entscheidungsberechtigte\n\n| Person | Berechtigung | Einschränkung |\n|--------|-------------|--------------|\n| Carlo | `approved`, `rejected` | — |\n| Agent | `rejected` (Duplikat/Konflikt-Flag) | Nur mit explizitem Hinweis, kein finales Approved |\n| System | `expired` (automatisch) | Nur Zeitablauf |\n\n---", "text_hash": "a1ed2a41e6073547d9fd4e16cae5e868f32e5b926f6aeb4748a84c9b31fea0fe"}, {"chunk_key": "2eb5b2202af74c1010b5fff520f59a15510bc50bdbe73c02560ea085ee65dc07", "chunk_index": 3, "heading": "Pflichtfelder: `approved`", "text": "| `rejected` (Duplikat/Konflikt-Flag) | Nur mit explizitem Hinweis, kein finales Approved |\n| System | `expired` (automatisch) | Nur Zeitablauf |\n\n---\n## Pflichtfelder: `approved`\n\nAlle folgenden Felder müssen gesetzt sein, bevor eine Review-Datei auf `approved` gesetzt wird.\nEin `approved` ohne vollständige Pflichtfelder ist ungültig.\n\n```json\n{\n  \"status\": \"approved\",\n  \"human_decision\": \"approved\",\n  \"decided_by\": \"carlo\",\n  \"decided_at\": \"2026-05-21T15:00:00Z\",\n  \"decision_note\": \"<Freitext — mindestens 20 Zeichen, Begründung Pflicht>\",\n  \"evidence\": {\n    \"type\": \"code_commit | deployment | document | verbal_confirmed\",\n    \"source\": \"<Pfad, URL, Commit-Hash oder Beschreibung>\",\n    \"verified_at\": \"2026-05-21T15:00:00Z\"\n  },\n  \"obsidian_write_allowed\": true,\n  \"graphiti_write_allowed\": false\n}\n```", "text_hash": "60922c58aae466bc635e46ac38703523986ef8a3e18be044104a0b22e02395e8"}, {"chunk_key": "77ead34447ebb7af750754bb540aa0494c7659c5d7966453fe962a3f327b9b1d", "chunk_index": 4, "heading": "Pflichtfeld-Regeln für `approved`:", "text": "mit-Hash oder Beschreibung>\",\n    \"verified_at\": \"2026-05-21T15:00:00Z\"\n  },\n  \"obsidian_write_allowed\": true,\n  \"graphiti_write_allowed\": false\n}\n```\n### Pflichtfeld-Regeln für `approved`:\n\n| Feld | Regel |\n|------|-------|\n| `decided_by` | Muss `\"carlo\"` sein — keine anderen Werte |\n| `decided_at` | ISO-8601 UTC — muss aktuell sein (nicht Vergangenheit > 24h) |\n| `decision_note` | Mindestens 20 Zeichen, erklärt warum approved |\n| `evidence.type` | Einer von vier erlaubten Typen |\n| `evidence.source` | Nicht leer — Pfad, Hash oder nachvollziehbare Referenz |\n| `obsidian_write_allowed` | Erst nach Setzen von `approved` auf `true` setzen |\n| `graphiti_write_allowed` | Bleibt `false` — wird erst nach Obsidian-Update auf `true` gesetzt |", "text_hash": "e08295cff6eb23e3cf5ff9d21723420b7ae32e59e94b20e87dd89916b5c7be61"}, {"chunk_key": "94ab583feb9bbda8ac6a5a09770643edc96cd0037e840412137250b2aeca6755", "chunk_index": 5, "heading": "Evidence-Typen:", "text": " Erst nach Setzen von `approved` auf `true` setzen |\n| `graphiti_write_allowed` | Bleibt `false` — wird erst nach Obsidian-Update auf `true` gesetzt |\n### Evidence-Typen:\n\n| Typ | Bedeutung | Beispiel |\n|-----|-----------|---------|\n| `code_commit` | Implementierung im Code nachweisbar | `\"git commit abc123 in vento/telegram_handler.py\"` |\n| `deployment` | Produktiv deployed und getestet | `\"Container vento neu deployed, Telegram-Test erfolgreich 2026-05-21\"` |\n| `document` | Schriftlich dokumentiert (E-Mail, Protokoll) | `\"E-Mail von Mario 2026-05-21: Telegram aktiv\"` |\n| `verbal_confirmed` | Mündlich bestätigt durch Entscheidungsträger | `\"Carlo bestätigt mündlich in Session 2026-05-21\"` |\n\n`verbal_confirmed` hat die niedrigste Beweiskraft. Bei Zweifel: `rejected`.\n\n---", "text_hash": "e5a3df680cde9ae08f31d206f0463223f99df23de8caa7f0eeb1620cedc6c3d5"}, {"chunk_key": "59caebccb4a49fb2dde4f81db8315d2a5be064b0dde0495ff8bc58ef7fca4ef3", "chunk_index": 6, "heading": "Pflichtfelder: `rejected`", "text": "eidungsträger | `\"Carlo bestätigt mündlich in Session 2026-05-21\"` |\n\n`verbal_confirmed` hat die niedrigste Beweiskraft. Bei Zweifel: `rejected`.\n\n---\n## Pflichtfelder: `rejected`\n\n```json\n{\n  \"status\": \"rejected\",\n  \"human_decision\": \"rejected\",\n  \"decided_by\": \"carlo | agent | system-expire-job\",\n  \"decided_at\": \"2026-05-21T15:00:00Z\",\n  \"decision_note\": \"<Freitext — Ablehnungsgrund Pflicht>\",\n  \"rejection_reason\": \"duplicate | no_evidence | conflict | outdated | irrelevant\",\n  \"duplicate_of\": \"<review_id | null>\",\n  \"obsidian_write_allowed\": false,\n  \"graphiti_write_allowed\": false\n}\n```", "text_hash": "9e20e9d547f31f54c9d62bf7b0f1442bbf40b898aade1e96ac558171dec1ed61"}, {"chunk_key": "b0f8bbe0358086fdfbcc9209d0beca2f81291f264d4cd5ad302dc45c08a68e4f", "chunk_index": 7, "heading": "Rejection-Reason-Werte:", "text": " conflict | outdated | irrelevant\",\n  \"duplicate_of\": \"<review_id | null>\",\n  \"obsidian_write_allowed\": false,\n  \"graphiti_write_allowed\": false\n}\n```\n### Rejection-Reason-Werte:\n\n| Wert | Bedeutung |\n|------|-----------|\n| `duplicate` | Inhaltlich identisch mit einem anderen Review-Eintrag |\n| `no_evidence` | Kein technischer oder dokumentarischer Nachweis vorhanden |\n| `conflict` | Widerspricht bestehendem, verifiziertem Obsidian-Inhalt |\n| `outdated` | Fakt ist veraltet oder inzwischen irrelevant |\n| `irrelevant` | Fakt gehört nicht in Obsidian (zu spezifisch, temporär, intern) |", "text_hash": "bef0a6cdf4d2261fd6a12e40942ef15623d33cfeb870de75fd26039a0967d012"}, {"chunk_key": "fa3ce30be4358eeece9d7c82fee2735ae09b4fd6bf845f83cbf2c13c6fc1eefd", "chunk_index": 8, "heading": "Pflichtfeld-Regeln für `rejected`:", "text": "alt |\n| `outdated` | Fakt ist veraltet oder inzwischen irrelevant |\n| `irrelevant` | Fakt gehört nicht in Obsidian (zu spezifisch, temporär, intern) |\n### Pflichtfeld-Regeln für `rejected`:\n\n| Feld | Regel |\n|------|-------|\n| `rejection_reason` | Muss gesetzt sein — einer der fünf Werte |\n| `decision_note` | Mindestens 10 Zeichen |\n| `duplicate_of` | Bei `rejection_reason: duplicate` zwingend gesetzt |\n| `obsidian_write_allowed` | Immer `false` |\n| `graphiti_write_allowed` | Immer `false` |\n\n---", "text_hash": "2cb32e1e52c24577b4a96a3caa3c0607d0cc41601bc71fa163f11403113c58a5"}, {"chunk_key": "70f84ee5b2ee6436f4e3612dd86b5ce883c4eaa3d1a6388993859d1d4b90d31d", "chunk_index": 9, "heading": "Umgang mit Duplikaten", "text": " | Bei `rejection_reason: duplicate` zwingend gesetzt |\n| `obsidian_write_allowed` | Immer `false` |\n| `graphiti_write_allowed` | Immer `false` |\n\n---\n## Umgang mit Duplikaten", "text_hash": "e4cf9da2c85f89adc596e7be49b86f0fe524ef586f6f67a03356eb57f185411e"}, {"chunk_key": "b7dabea38a26ac5c60f6afcc2c974db86a8e2461ebb4c8f92c0042b1f93d8f4e", "chunk_index": 10, "heading": "Definition:", "text": "## Umgang mit Duplikaten\n### Definition:\nZwei Review-Einträge sind Duplikate wenn ihr `proposed_fact` inhaltlich identisch ist,\nunabhängig von Groß-/Kleinschreibung und Satzzeichen.", "text_hash": "f381187dfbdb2edb4e254760ec663908ffc34e3aee06ffd10856805cc42ed93d"}, {"chunk_key": "925eab1c94409dca937ed3e21549206bc2ebc7659f690a627e104d05caaefec8", "chunk_index": 11, "heading": "Vorgehen:", "text": "finition:\nZwei Review-Einträge sind Duplikate wenn ihr `proposed_fact` inhaltlich identisch ist,\nunabhängig von Groß-/Kleinschreibung und Satzzeichen.\n### Vorgehen:\n\n1. Den **ältesten** Eintrag als primären Review behalten → prüfen, entscheiden\n2. Alle neueren Duplikate → `rejected` mit:\n   - `rejection_reason: \"duplicate\"`\n   - `duplicate_of: \"<älteste review_id>\"`\n   - `decision_note: \"Duplikat von <review_id>\"`", "text_hash": "0c57c143160e42bd95ae49b0fdeb425003d23c54ff4e4b4dd26662d98a6d9cb3"}, {"chunk_key": "9ef3ecf7445e290fb62479cfb0978501d72e9e4ed24b2e4010805663007abd8d", "chunk_index": 12, "heading": "Duplikat-Prüfung vor jedem Approve:", "text": "ate → `rejected` mit:\n   - `rejection_reason: \"duplicate\"`\n   - `duplicate_of: \"<älteste review_id>\"`\n   - `decision_note: \"Duplikat von <review_id>\"`\n### Duplikat-Prüfung vor jedem Approve:\n```bash\ngrep -r \"proposed_fact\" /opt/struktur/memory/review-queue/ | sort\n```", "text_hash": "449980b365d7ccf9a0adf58be3dabe25063b0afb8407444c8cd9a75587aae56a"}, {"chunk_key": "473e9d27389950b93bc5ae10379d2107d54cc425eb49b4c2743852e6901d0726", "chunk_index": 13, "heading": "Beispiel-Vorgehen:", "text": "### Duplikat-Prüfung vor jedem Approve:\n```bash\ngrep -r \"proposed_fact\" /opt/struktur/memory/review-queue/ | sort\n```\n### Beispiel-Vorgehen:\n\n```\nREVIEW-A (ältester, pending) → prüfen und entscheiden\nREVIEW-B (jünger, gleicher Fakt) → rejected, duplicate_of: REVIEW-A\nREVIEW-C (noch jünger) → rejected, duplicate_of: REVIEW-A\n```\n\nNur REVIEW-A erhält die inhaltliche Entscheidung.\n\n---", "text_hash": "3c8e9e9acf16b2ae033509986474b294877a763e31a45dedd37c325ee68023ee"}, {"chunk_key": "10ea88e3cbd0e31dcba8bed7ffa5494e0ebe62bf83d42c6a14602ffd56eb1558", "chunk_index": 14, "heading": "Umgang mit Konflikten", "text": "rejected, duplicate_of: REVIEW-A\nREVIEW-C (noch jünger) → rejected, duplicate_of: REVIEW-A\n```\n\nNur REVIEW-A erhält die inhaltliche Entscheidung.\n\n---\n## Umgang mit Konflikten", "text_hash": "899b237be5fd6c5212708a9bd54f526ac938020abc8bdac3e53525a7c3c899eb"}, {"chunk_key": "e532e3ae3aa063617afe2ea9e2fbdeb747d76ca88b22888aa34a1e2de6717c2d", "chunk_index": 15, "heading": "Definition:", "text": "## Umgang mit Konflikten\n### Definition:\nEin proposed_fact ist in Konflikt wenn er einem verifizierten, aktuellen Obsidian-Eintrag\ninhaltlich widerspricht — nicht nur ergänzt.", "text_hash": "0afb61f93b6b8e50c2145ed91fd791ff2d3c1840b56e42faad215b1401f8198a"}, {"chunk_key": "5a5ea22e561d3ea7d911d6d73a7808de4f2efbdf1f496edf8a9b95b6dbea96ef", "chunk_index": 16, "heading": "Vorgehen bei Konflikt:", "text": "### Definition:\nEin proposed_fact ist in Konflikt wenn er einem verifizierten, aktuellen Obsidian-Eintrag\ninhaltlich widerspricht — nicht nur ergänzt.\n### Vorgehen bei Konflikt:\n\n**Fall 1: Obsidian ist korrekt, proposed_fact ist falsch**\n→ `rejected`, `rejection_reason: \"conflict\"`, `decision_note` erklärt welcher Obsidian-Inhalt widerspricht\n\n**Fall 2: proposed_fact ist korrekt, Obsidian ist veraltet**\n→ Erst Obsidian manuell korrigieren\n→ Dann neuen Review-Eintrag erstellen mit `fact_status: \"verified\"` und Nachweis\n→ Den alten Konflikt-Eintrag: `rejected`, `rejection_reason: \"outdated\"`, `decision_note: \"Obsidian wurde korrigiert, neuer Review REVIEW-XYZ\"`\n\n**Fall 3: Unklar**\n→ `rejected`, `rejection_reason: \"conflict\"`, `decision_note` beschreibt Unklarheit\n→ Klärung abwarten, dann neu einreichen\n\n---", "text_hash": "9521e8ba7fb3ba5f057d64799f5428c489e0c49295d0a3fd3753fde9630444ea"}, {"chunk_key": "4c889403d397b922879f3659152088652d8308aadfe4a7075923cc6d77491b51", "chunk_index": 17, "heading": "Was nach Approval manuell in Obsidian geändert werden darf", "text": "`\n\n**Fall 3: Unklar**\n→ `rejected`, `rejection_reason: \"conflict\"`, `decision_note` beschreibt Unklarheit\n→ Klärung abwarten, dann neu einreichen\n\n---\n## Was nach Approval manuell in Obsidian geändert werden darf", "text_hash": "95dbfc964f6a2b1eee353ddbae1d34a5968e42ee48ec6f2cdac1fc75cbe2be55"}, {"chunk_key": "df2ffa283e8b94d1375e919eb35b148248048147028c5d24dac8d043251ad24d", "chunk_index": 18, "heading": "Freigegeben nach `approved`:", "text": "## Was nach Approval manuell in Obsidian geändert werden darf\n### Freigegeben nach `approved`:\n- Den in `proposed_fact` beschriebenen Sachverhalt in der `proposed_obsidian_page` ergänzen\n- Datum der Änderung in der Obsidian-Datei vermerken\n- Quelle/Nachweis als Fußnote oder Kommentar ergänzen", "text_hash": "043ded2d58305bb0d20c36f451cb54190642bbb637782464d4754be8edbfb121"}, {"chunk_key": "6d42888ee6e731732774f8a1c67583490108f34e3a0022608c501b267e5dc65f", "chunk_index": 19, "heading": "Nicht erlaubt:", "text": "lt in der `proposed_obsidian_page` ergänzen\n- Datum der Änderung in der Obsidian-Datei vermerken\n- Quelle/Nachweis als Fußnote oder Kommentar ergänzen\n### Nicht erlaubt:\n- Andere Obsidian-Seiten als `proposed_obsidian_page` ändern\n- Den Fakt umformulieren (nur exakt das was approved wurde)\n- Weitere Fakten \"mitzuschreiben\" ohne eigenen Review-Eintrag", "text_hash": "e626e7d1d9474809786a3147f8608945432a04b32f62bca5668c86cb1b27c3ef"}, {"chunk_key": "70ec96494b972b6537f2c9c7f93c6c1348f29fbcd7da6d8507970b64f0cd74e5", "chunk_index": 20, "heading": "Nach dem Obsidian-Update:", "text": "oposed_obsidian_page` ändern\n- Den Fakt umformulieren (nur exakt das was approved wurde)\n- Weitere Fakten \"mitzuschreiben\" ohne eigenen Review-Eintrag\n### Nach dem Obsidian-Update:\n1. Review-Datei öffnen\n2. Felder ergänzen:\n   ```json\n   {\n     \"obsidian_updated_at\": \"2026-05-21T15:30:00Z\",\n     \"obsidian_updated_by\": \"carlo\",\n     \"obsidian_section_updated\": \"Technik\",\n     \"graphiti_write_allowed\": true\n   }\n   ```\n3. Review-Datei speichern\n\n---", "text_hash": "b49072ab7f491476f94a5af65554742fe01fa18c270b858a1710c7f57a2900eb"}, {"chunk_key": "c4f61b3e0860623d888a18c17bd87a8b4d492d97d31a92adc6332586a77f3269", "chunk_index": 21, "heading": "Wann Graphiti neu importiert werden darf", "text": "idian_updated_by\": \"carlo\",\n     \"obsidian_section_updated\": \"Technik\",\n     \"graphiti_write_allowed\": true\n   }\n   ```\n3. Review-Datei speichern\n\n---\n## Wann Graphiti neu importiert werden darf", "text_hash": "91d35c9ee6e21f67976bd9674726603420f101abbac9a313014d3703e7e4ef39"}, {"chunk_key": "8cef9c00618af8241576789401988c30cbf848929e31816411ce24e9332ca2ec", "chunk_index": 22, "heading": "Voraussetzungen (alle müssen erfüllt sein):", "text": "## Wann Graphiti neu importiert werden darf\n### Voraussetzungen (alle müssen erfüllt sein):\n1. Review-Status = `approved`\n2. Obsidian-Update abgeschlossen (`obsidian_updated_at` gesetzt)\n3. `graphiti_write_allowed: true` in der Review-Datei\n4. Keine offenen Konflikte mit anderen Reviews zum gleichen Projekt", "text_hash": "04b927c1fd9136ea75c71de36963cebd770d1da808627331c8b1ff748cb082a0"}, {"chunk_key": "44e9dda250340590a07f496c752ebcdcf053c30d01feb2f1b30cec952b5a4322", "chunk_index": 23, "heading": "Methode:", "text": "bsidian_updated_at` gesetzt)\n3. `graphiti_write_allowed: true` in der Review-Datei\n4. Keine offenen Konflikte mit anderen Reviews zum gleichen Projekt\n### Methode:\n```bash\npython3 /opt/struktur/graphiti/import_obsidian.py\n```\n\nKein direkter API-Write über das Tool-Gateway.\nGraphiti-Import liest aus Obsidian — nicht aus der Review-Queue.", "text_hash": "a98a3fa5f549ed7ab74a36cf5b0c2915d3025dc98f190264a9ea4391963e6d49"}, {"chunk_key": "00cb9b794dd062d8c93f713bd2f525d60c4b5708db006ee16213b531a2677e10", "chunk_index": 24, "heading": "Nach dem Graphiti-Import:", "text": "uktur/graphiti/import_obsidian.py\n```\n\nKein direkter API-Write über das Tool-Gateway.\nGraphiti-Import liest aus Obsidian — nicht aus der Review-Queue.\n### Nach dem Graphiti-Import:\nReview-Datei ergänzen:\n```json\n{\n  \"graphiti_imported_at\": \"2026-05-21T16:00:00Z\"\n}\n```\n\nReview-Datei in `/opt/struktur/memory/review-queue/archived/` verschieben.\n\n---", "text_hash": "98b5f6bb4fd056fb7de8b2ed9e2340973e7fd518d07654176dea42aea69586ac"}, {"chunk_key": "aefd768968d02b004cd2ace569a4f55e87df67b482e3126d7b730258084fb575", "chunk_index": 25, "heading": "Vollständiger Entscheidungs-Workflow", "text": "zen:\n```json\n{\n  \"graphiti_imported_at\": \"2026-05-21T16:00:00Z\"\n}\n```\n\nReview-Datei in `/opt/struktur/memory/review-queue/archived/` verschieben.\n\n---\n## Vollständiger Entscheidungs-Workflow\n\n```\n[1] Review-Queue prüfen\n    ls /opt/struktur/memory/review-queue/\n\n[2] Duplikate identifizieren und als rejected markieren\n\n[3] Für jeden verbleibenden pending Review:\n    a) Konflikt-Check (Obsidian lesen)\n    b) Quellen-Check (Evidence vorhanden?)\n    c) Entscheidung treffen\n\n[4a] Bei approved:\n    - Pflichtfelder setzen (decided_by, decided_at, decision_note, evidence)\n    - obsidian_write_allowed: true setzen\n    - Obsidian manuell aktualisieren\n    - obsidian_updated_at setzen\n    - graphiti_write_allowed: true setzen\n    - Graphiti-Import ausführen\n    - graphiti_imported_at setzen\n    - Datei nach /archived/ verschieben\n\n[4b] Bei rejected:\n    - Pflichtfelder setzen (decided_by, decided_at, decision_note, rejection_reason)\n    - obsidian_write_allowed: false (unveränderlich)\n    - graphiti_write_allowed: false (unveränderlich)\n    - Datei nach /archived/ verschieben\n\n[5] Audit-Nachweis:\n    - Review-Datei selbst ist Audit-Dokument\n    - expire_audit.jsonl für automatische Abläufe\n```\n\n---", "text_hash": "43001e3ca6eb6bcc731c4f4d4d40f2bbecd6a14437da5c14c421cb6cdbc29017"}, {"chunk_key": "42357a53a5decae062d251528dd073b32ecfa103f08d62857c9f565ca73229f1", "chunk_index": 26, "heading": "Testfall: Duplikat-Rejection", "text": "ch /archived/ verschieben\n\n[5] Audit-Nachweis:\n    - Review-Datei selbst ist Audit-Dokument\n    - expire_audit.jsonl für automatische Abläufe\n```\n\n---\n## Testfall: Duplikat-Rejection\n\n**Situation:**\n- `REVIEW-20260521-211959-3ab207` — primärer Eintrag (ältester), `pending`\n- `REVIEW-20260521-212610-4cc5d9` — Duplikat, gleicher Fakt, `pending`\n\n**Vorgehensweise:**\n\nSchritt 1: Duplikat identifizieren\n```bash\npython3 -c \"\nimport json\nfrom pathlib import Path\nq = Path('/opt/struktur/memory/review-queue')\nfor fp in sorted(q.glob('REVIEW-2026*.json')):\n    r = json.load(open(fp))\n    print(fp.name, '|', r['status'], '|', r['proposed_fact'][:60])\n\"\n```\n\nErwartete Ausgabe:\n```\nREVIEW-20260521-211959-3ab207.json | pending | Vento unterstuetzt Telegram als zusaetzlichen Kanal\nREVIEW-20260521-212610-4cc5d9.json | pending | Vento unterstuetzt Telegram als zusaetzlichen Kanal\n```\n\nSchritt 2: Duplikat manuell auf `rejected` setzen\n\nInhalt für `REVIEW-20260521-212610-4cc5d9.json`:\n```json\n{\n  \"status\": \"rejected\",\n  \"human_decision\": \"rejected\",\n  \"decided_by\": \"carlo\",\n  \"decided_at\": \"<JETZT ISO-8601>\",\n  \"decision_note\": \"Duplikat von REVIEW-20260521-211959-3ab207. Gleicher Fakt, gleiche Session, Turn 2 vs Turn 1.\",\n  \"rejection_reason\": \"duplicate\",\n  \"duplicate_of\": \"REVIEW-20260521-211959-3ab207\",\n  \"obsidian_write_allowed\": false,\n  \"graphiti_write_allowed\": false\n}\n```\n\nSchritt 3: Primären Review (`-3ab207`) weiter bearbeiten\n→ Quellen-Check → noch kein Nachweis → bleibt `pending`\n\n**Erwartetes Ergebnis:**\n```\nREVIEW-20260521-211959-3ab207: pending  (wartet auf Nachweis)\nREVIEW-20260521-212610-4cc5d9: rejected (duplicate)\n```\n\n---", "text_hash": "90f33fbdf9830455a9a22daf58e6ff1ac587fc34c1d42b12bcaee8ff01a69168"}, {"chunk_key": "44f1e845c9497c35b9f1fa479fb4481966ca1ccff6c035ec253e8dff9f0d2b61", "chunk_index": 27, "heading": "Risiken", "text": "*Erwartetes Ergebnis:**\n```\nREVIEW-20260521-211959-3ab207: pending  (wartet auf Nachweis)\nREVIEW-20260521-212610-4cc5d9: rejected (duplicate)\n```\n\n---\n## Risiken\n\n| Risiko | Impact | Mitigation |\n|--------|--------|-----------|\n| Approve ohne Evidence | Hoch | `evidence.source` Pflichtfeld — nicht leer |\n| Zwei parallele Approvals für Duplikate | Mittel | Duplikat-Check vor jedem Approve |\n| Obsidian-Update ohne `obsidian_updated_at` | Mittel | Checkliste im Workflow |\n| Graphiti-Import vor Obsidian-Update | Hoch | `graphiti_write_allowed` bleibt `false` bis `obsidian_updated_at` gesetzt |\n| `decided_by` wird gefälscht | Niedrig | Audit-Datei + Review-Datei zusammen — Inkonsistenz erkennbar |\n| Reject wird rückgängig gemacht ohne Grund | Mittel | Reaktivierung erfordert `decision_note` mit Begründung |\n\n---", "text_hash": "b4fa7d621321612f3572a1c8d8e4c9d46ac0f8612f065292c1fed09414ee8f1e"}, {"chunk_key": "d47fbaa64c238ad2c22ea41daefb8e22399c5b767f14602bf3bda2877804b7ca", "chunk_index": 28, "heading": "Nächster Schritt", "text": "sammen — Inkonsistenz erkennbar |\n| Reject wird rückgängig gemacht ohne Grund | Mittel | Reaktivierung erfordert `decision_note` mit Begründung |\n\n---\n## Nächster Schritt\n\n**Keine weiteren Automatisierungen geplant.**\n\nEmpfehlung: Review-Queue manuell abarbeiten wenn Fakten-Qualität steigt\n(mehr verifizierte Informationen aus realen Entscheidungen).\n\nOptional Phase 2b: Review-Liste im Projects-Dashboard anzeigen (READ-only, kein Approve-Button).\n\n---\n\n*Protokollpfad: `/opt/struktur/hermes-carlo/review/REVIEW_DECISION_PROTOCOL.md`*\n*Verwandte Dokumente:*\n- *`REVIEW_QUEUE_WORKFLOW.md` — Statusmodell und Auto-Expire*\n- *`AGENT_ORCHESTRATION_PROTOCOL.md` — wie Reviews entstehen*\n- *`SESSION_MEMORY_ARCHITECTURE.md` — Datenmodell*", "text_hash": "68a1809c3d7164099450c375b0630b40c9df3c71c88784fa328e68ce43da7588"}], "elapsed_s": 0.08}