class RuleEngine {
private state: SessionState = {
role: 'default',
toolCallCount: 0,
overrideCount: 0,
maxOverrides: 3,
lastConfirmation: null,
}
}
ADR-011: In-memory Session-State (keine Persistenz)
Status
Accepted
Context
Das Plugin speichert während einer opencode-Session Zustandsinformationen:
- toolCallCount — Anzahl der Tool-Calls seit letztem Reset
- overrideCount — Anzahl der Bypass-Nutzungen
- maxOverrides — Maximal erlaubte Bypässe
- role — Aktive Agent-Rolle
- lastConfirmation — Zeitstempel der letzten Bestätigung
Die Frage: Wo lebt dieser State? In-memory (flüchtig, verfällt beim Neustart) oder persistent (Datenbank, Datei)?
Alternatives Considered
Option A: In-memory (gewählt)
Session-State lebt ausschliesslich im RAM des opencode-Prozesses.
Vorteile: - Einfach — kein DB-Schema, kein I/O, keine Latenz - Schnell — State-Zugriff ist <1ms (RAM vs. Disk/Netzwerk) - Keine externen Abhängigkeiten — keine Datenbank, keine Datei-I/O - Privacy-freundlich — State verfällt beim Session-Ende, kein PII auf Disk - GDPR-konform — keine persistenten Logs mit Personenbezug (siehe 02-architecture-constraints.md) - Fail-Open — kein DB-Connection-Error möglich
Nachteile: - State geht verloren bei opencode-Crash oder -Neustart - Kein Cross-Session-State — toolCallCount startet bei jeder Session bei 0 - Kein Audit-Trail — nach Session-Ende sind Override-Counts weg - Keine Shared-State zwischen Plugins (wenn mehrere Instanzen)
Option B: File-basierte Persistenz (JSON/YAML)
State wird nach jeder Änderung in eine JSON-Datei geschrieben.
Vorteile: - State überlebt Neustart - Einfach zu implementieren (JSON.stringify + writeFile) - Keine externe DB nötig
Nachteile: - I/O-Latenz — jeder Tool-Call müsste State persistieren - Race Conditions — wenn opencode den Prozess killt, während geschrieben wird - File-Locking — bei parallelen Sessions (theoretisch möglich) - PII auf Disk — overrideCount + toolCallCount sind nicht PII, aber der Audit-Kontext könnte es sein - Kein echter Vorteil — opencode startet Plugin bei jeder Session neu, State wäre nach Session-Ende irrelevant - Zusätzliche Komplexität für geringen Nutzen
Option C: SQLite / Datenbank
State wird in einer SQLite-Datenbank persistiert.
Vorteile: - Robuste Persistenz - Query-Möglichkeit (Audit: "wie oft wurde gebypasst?")
Nachteile: - Überdimensioniert — 4 Integer + 1 String + 1 Date = keine Rechtfertigung für SQLite - Zusätzliche Dependency (better-sqlite3 oder ähnlich) - I/O-Latenz bei jedem Tool-Call - Nicht im Scope (Architecture Constraints: "No database") - Kein Use Case — Audit-Logs sind explizit NICHT im Plugin-Scope (siehe 02-architecture-constraints.md: "Plugin must not be used for performance monitoring")
Evaluation Criteria
| Kriterium | Gewicht | Beschreibung |
|---|---|---|
Einfachheit |
Hoch |
Keine zusätzlichen Komponenten oder Dependencies |
Geschwindigkeit |
Hoch |
State-Zugriff <1ms |
Datenschutz |
Mittel |
Keine PII auf Disk |
Persistenz |
Niedrig |
State muss Session nicht überleben |
Audit-Fähigkeit |
Niedrig |
Audit ist opencode’s Aufgabe, nicht Plugin |
Decision
Option A: In-memory wurde gewählt.
Begründung:
- Das Plugin hat keinen Use Case für persistierenden State: toolCallCount ist rein session-bezogen
- Zusätzliche Persistenz würde Komplexität ohne messbaren Nutzen hinzufügen
- Datenschutz: In-memory State verfällt beim Session-Ende — kein PII auf Disk
- Performance: RAM-Zugriff ist <1ms (vs. Disk I/O oder DB-Query)
- Architecture Constraint: "No database" ist bereits definiert
- Audit-Trail ist nicht im Plugin-Scope; opencode selbst loggt Tool-Calls
Was passiert bei Neustart?
| Situation | State | Konsequenz |
|---|---|---|
opencode normal beenden |
Verloren |
Nächste Session startet bei 0 |
opencode crasht |
Verloren |
Keine Datenkorruption (nichts zu korrumpieren) |
Plugin neu laden |
Verloren |
Config reload resettet toolCallCount |
Config-Rest ( |
Zurückgesetzt |
toolCallCount = 0, overrideCount = 0 |
opencode-Upgrade |
Verloren |
Keine Migrationsprobleme |
State-Struktur
interface SessionState {
role: string // aktive Rolle (default: "default")
toolCallCount: number // Tool-Calls seit letztem Reset
overrideCount: number // Bypass-Nutzungen in dieser Session
maxOverrides: number // aus Config (kann sich bei Reload ändern)
lastConfirmation: Date | null // letzte "Weiter?"-Bestätigung
}
Consequences
-
Positiv: Maximale Einfachheit — kein DB, kein I/O, keine Datei-Locks
-
Positiv: Maximale Performance — RAM-Zugriff <1ms
-
Positiv: Datenschutz — keine PII auf Disk, State verfällt mit Session
-
Positiv: Keine zusätzlichen Dependencies
-
Negativ: State geht bei opencode-Crash verloren (letzter overrideCount weg)
-
Negativ: Kein Cross-Session-Audit ("wie oft wurde heute gebypasst?")
-
Negativ: Session-übergreifende Einstellungen (z.B. "Rolle dauerhaft wechseln") nicht möglich
-
Trade-off: Persistenz gegen Einfachheit — Einfachheit gewinnt, weil der Use Case keinen persistenten State erfordert
Related
-
SessionState in 05-building-block-view.md (Interface-Definition)
-
06-runtime-view.md (Session Reset)
-
02-architecture-constraints.md ("No database")
-
01-introduction-and-goals.md (GDPR/Data Privacy — PII nicht persistent)
Sources
-
opencode Plugin SDK — Plugin Lifecycle: https://opencode.ai/docs/plugins (Plugin wird bei jeder Session neu geladen)
-
Architecture Constraints — No database: https://github.com/JensGrote/opencode-semantic-anchors/blob/main/docs/02-architecture-constraints.md
-
GDPR Article 5(1)(c) — Data minimisation
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.