ADR-008: Fail-Open Prinzip bei Hook-Fehlern

Status

Accepted

Context

Das Plugin operiert innerhalb des opencode-Prozesses. Wenn ein Plugin-Hook fehlschlägt (Crash, unerwarteter Fehler, Config-Bug), gibt es zwei grundsätzliche Strategien:

  1. Fail-Open — Bei Fehler wird die Tool-Ausführung erlaubt (der Agent läuft weiter)

  2. Fail-Closed — Bei Fehler wird die Tool-Ausführung blockiert (der Agent stoppt)

Das Problem: - Das Plugin ist ein steering tool, kein Sicherheitslayer - Ein blockierender Fehler würde die gesamte opencode-Session lahmlegen - Der User kann das Plugin nicht "ausschalten" wenn es crasht (ausser Neustart) - Gleichzeitig: Wenn das Plugin wegen eines Fehlers nicht blockt, obwohl es sollte, entsteht ein "silent ignore"-Problem

Die Frage: Reagieren wir bei Fehlern permissiv (allow) oder restriktiv (block)?

Alternatives Considered

Option A: Fail-Open (gewählt)

Bei jedem Fehler in einem Hook gibt das Plugin { allow: true } zurück (im neuen API: kein throw, tool läuft durch). Der Fehler wird geloggt.

Fehler in Config Layer     → DefaultConfig + Warning-Log
Fehler in RuleEngine       → allow: true + Error-Log
Fehler in Hook Handler     → allow: true + Error-Log
Fehler in Custom Tool      → Error-Response an User

Vorteile: - Workflow Continuity — Der Agent läuft immer weiter, auch bei Plugin-Fehlern - Graceful Degradation — Bei Teilausfällen laufen andere Contracts weiter - Debuggability — Fehler werden geloggt, können analysiert werden - Benutzerfreundlich — Kein "Plugin abgestürzt, Session tot"-Szenario

Nachteile: - Silent Failure Risk — Ein Bug im Plugin führt dazu, dass Regeln nicht durchgesetzt werden, ohne dass der User es merkt - Security-Theater — User vertraut auf Enforcement, aber bei Fehlern läuft alles durch

Option B: Fail-Closed

Bei jedem Fehler blockt das Plugin die Tool-Ausführung.

Vorteile: - Security-First — Im Zweifel blocken, keine silent failures - Auffällig — User merkt sofort, dass das Plugin ein Problem hat

Nachteile: - Workflow Kill — Ein Plugin-Fehler blockiert die gesamte opencode-Session - Keine Graceful Degradation — Schon ein kaputter Contract legt alles lahm - Frustrierend — User kann nicht arbeiten, bis das Plugin gefixt ist - Plugin ist nicht als Sicherheitslayer designed — Fail-Closed suggeriert eine Sicherheitsgarantie, die das Plugin nicht halten kann

Option C: Hybrid — Fail-Open mit User-Notification

Fail-Open, aber der User wird bei jedem Fail-Open durch eine UI-Message informiert.

Nachteile: - opencode Plugin SDK bietet keine Möglichkeit, UI-Messages zu senden - client.app.log() schreibt nur ins Log — User sieht es nicht direkt - Müsste über TUI-Notification realisiert werden (client.tui.showToast() — API unklar) - User-Notification ist nice-to-have, aber nicht zuverlässig genug für Fail-Closed

Evaluation Criteria

Kriterium Gewicht Beschreibung

Workflow Continuity

Hoch

Der Agent muss weiterlaufen können

Sicherheit

Mittel

Keine falsche Sicherheitsgarantie

Fehlererkennbarkeit

Mittel

User muss Fehler bemerken können (via Log)

Graceful Degradation

Hoch

Teilausfall = Teilfunktion, nicht Totalausfall

Implementierungsaufwand

Niedrig

Einfach zu implementieren (catch + log)

Decision

Option A: Fail-Open wurde gewählt.

Begründung: - Das Plugin ist ein runtime steering tool, kein Sicherheitslayer. Fail-Open ist die ehrlichere Strategie. - Workflow Continuity ist Quality Goal #3 — ein Plugin-Crash darf die Session nicht killen - Silent Failures werden durch Logging adressiert (client.app.log({ level: "error" })) - Fail-Open ist opencode’s eigener Ansatz (bei throw im Hook tool.execute.before fängt opencode den Fehler und lässt die Ausführung weiterlaufen) - Fail-Closed würde eine Sicherheitsgarantie suggerieren, die das Plugin nicht leisten kann

Protection Layers

Fail-Open wird auf zwei Ebenen implementiert:

Layer 1: Hook Handler fängt RuleEngine-Fehler  → catch + log + kein throw
Layer 2: opencode selbst fängt Hook-Fehler     → tool.execute läuft weiter

Consequences

  • Positiv: Der Agent läuft immer weiter (Quality Goal #3)

  • Positiv: Graceful Degradation — bei Config-Fehler laufen andere Contracts

  • Positiv: Einfach zu implementieren (jeder Hook ist in try/catch)

  • Positiv: opencode unterstützt dieses Pattern nativ (fängt throw im Hook)

  • Negativ: Bei einem Plugin-Bug werden Regeln still nicht durchgesetzt

  • Negativ: User muss aktiv ins Log schauen, um Fehler zu erkennen

  • Negativ: Fehlerhafte Config fällt nicht sofort auf (DefaultConfig wird geladen)

  • Trade-off: Workflow Continuity gegen Enforcement-Zuverlässigkeit — Continuity gewinnt, weil das Plugin kein Sicherheitslayer ist

  • Error Handling in 05-building-block-view.md (Section "Error Handling")

  • 06-runtime-view.md (Error Scenarios — Hook Throw-Error)

  • Quality Goal #3: Workflow Continuity in 01-introduction-and-goals.md

  • ADR-001: Enforcement via tool.execute.before

Sources