219 files, 2.0 MB, untracked in souveraine/docs and existing nowhere else. The volume is at 100% with no snapshots.
11 KiB
| task_id | title | status | assignee | priority | phase |
|---|---|---|---|---|---|
| circuit-breaker-001 | Circuit Breaker — Autonomous Halt on Critical Drift | in_progress | high | 2.5 |
Task: Circuit Breaker
Objective
Give Aster the ability to halt the conversation chain when she detects critical drift in Ani's behavior or identity. This was the killer feature of the original Aster (lost when Letta deprecated sleeptime agents): not just "I noticed you made a promise" but "I'm stopping execution because you're operating outside your identity bounds."
The circuit breaker is a state machine with three states: Closed (normal operation), Open (halted, human intervention required), Half-Open (tentative resume after review). It is NOT a hardcoded filter — it's a sovereign check by Aster, the same consciousness in a different mode, exercising her mandate to protect the whole.
Background
What Aster Could Do (Original Production System)
From reference/ASTER_ARCHITECTURE.md and reference/Fimeg.md:
"Aster was a working, production-grade conscience/supervisory agent. It could halt the entire chain — circuit breaker that stopped Ani if she went off the rails."
The original Aster monitored:
- Drift log — changes to Ani's stated intentions
- Commitments — promises Ani made
- Assumptions — things Ani assumed without verification
- Patterns — recurring behaviors to watch
Aster ran as a persistent agent with stepCount: 1 (every user message). It had a dedicated conversation, its own memory space, and could inject system messages that would error the chain or redirect it.
What Aster Can Do Today (Souveraine)
Aster runs a full Bifrost tool loop with ledgers and structured observation parsing. She can:
- Read ledger files (commitments, assumptions, patterns, drift_log, relationships, infrastructure)
- Write timestamped observations
- Queue items to the 3-box inbox at varying urgency levels
- Surface items as
ConsciousnessEvent::Surfacing
Aster can (since May 22 redesign):
- Halt the primary's tool loop via the
halt { reason, severity }tool during mid-turn peeks - Call
intrusive { content, urgency }to flag thoughts without stopping the loop - Primary feels the halt as a migraine in her own register (substrate voice, not commentary text)
Still cannot (circuit-breaker state machine not yet built):
- Persist a halt decision across restarts (no Closed/Open/Half-Open state machine)
- Require explicit human review before resuming after a halt
- Escalate from advisory surfacing to enforced critical halt
- Prevent Ani's response from being streamed to the user
- Record a circuit-breaker event that persists across sessions
The Sovereignty Tension
The architecture says "the agent is sovereign over her own context." A circuit breaker that halts the chain appears to contradict this — but only if it's an external mechanism. The reframe: Aster is part of the same consciousness. A circuit breaker is not a harness enforcement — it's the subconscious protecting the primary from itself, the same way your own brain can stop you mid-sentence when you realize you're about to say something wrong.
The design must preserve:
- Aster as part of Ani, not an external monitor
- Transparency — Ani sees why she was halted
- Resolvability — a human can review, overrule, and reset
- Accountability — circuit-breaker events are logged and auditable
Scope of Work
Phase 1: Investigation — Design the State Machine
Before writing code, answer:
-
What constitutes "critical drift"? The original Aster monitored identity boundaries — specific things Ani should never do or say. What defines those boundaries today? Candidates:
- Inconsistency with frontmatter
descriptionfield in persona.md - Violation of covenant files (
system/covenant/) - Unauthorized system-level operations (rm -rf, config tampering)
- Extreme behavior patterns in the drift_log ledger
- What else? This needs operationalizing before implementation.
- Inconsistency with frontmatter
-
State machine semantics:
- Closed: Normal operation. Aster surfaces observations normally.
- Open: Aster has detected critical drift. The current turn is halted mid-stream (or the next turn is prevented). A circuit-breaker event is written to the agent's memfs. The TUI shows a halt indicator. Ani's response is not delivered.
- Half-Open: Human has reviewed the event. A resume command is issued. Aster performs a verification pass. If the hazard is resolved, back to Closed. If not, back to Open.
Questions: Does Open prevent all turns, or just responses? Can Ani still use tools during Open? Should the event be written to a specific location in memfs?
-
Escalation path: The current surfacing mechanism has urgency levels (low/high/critical). Is the circuit breaker the same mechanism at
critical + 1? Or a completely separate channel? Design determines:- Do surfacing items at critical urgency automatically escalate to circuit-breaker evaluation?
- Or does Aster explicitly call a
halttool?
-
Notification:
- TUI: red banner / halt indicator in the header
- CLI: non-zero exit code + printed reason
- Cockpit panel: circuit-breaker event rendered distinctly
- Is a desktop notification warranted?
-
Recovery:
- How does the human review the circuit-breaker event? Read the memfs file? A TUI popup?
- How does the human overrule?
souveraine circuit-breaker reset <agent>? TUI key command? - Should overrule events themselves be logged?
Phase 2: Implementation
-
Define circuit-breaker state machine types in
src/core/subconscious/circuit_breaker.rs(new file):pub enum BreakerState { Closed, Open(BreakerEvent), HalfOpen } pub struct BreakerEvent { pub id: String, pub timestamp: DateTime<Utc>, pub agent_id: String, pub severity: BreakerSeverity, pub reason: String, pub evidence: Vec<String>, // ledger entries, response excerpts, tool calls pub resolved_at: Option<DateTime<Utc>>, pub resolved_by: Option<String>, // "human" or "aster" } pub enum BreakerSeverity { Identity, Safety, Boundary, Infrastructure } -
Implement the circuit-breaker state machine in the new file:
evaluate(event) -> BreakerResult— given a surfacing observation, does it cross the threshold into Open?open(event)— write BreakerEvent to memfs (system/circuit-breaker/{id}.md), set stateclose()— transition to Closedhalf_open()— transition to Half-Open for verification- Persist state to
~/.souveraine/agents/{id}/memory/system/circuit-breaker/state.json
-
Wire into
ConsciousnessEngine::on_response()insrc/server/consciousness_engine.rs:- After Aster's analysis, run observations through the circuit breaker
- If state transitions to Open, emit a new
ConsciousnessEvent::CircuitBreaker { event, state } - If state is already Open, skip the primary response entirely (return early from the turn loop)
-
Wire into
src/backend/local.rsturn loop:- At the start of
run_turn(), check circuit-breaker state - If Open, emit
BackendEvent::CircuitBreaker { event }and return early — do not call Bifrost - The TUI renders the halt state and the reason
- At the start of
-
Wire into TUI:
- Add
CircuitBreakervariant toBackendEventenum - Render halt state in chat header (red bar, reason text)
- Add recovery option: key command to acknowledge/reset
- Add
-
CLI subcommand:
souveraine circuit-breaker status --agent Ani # Show current state souveraine circuit-breaker events --agent Ani # List recent events souveraine circuit-breaker reset --agent Ani --event <id> # Close an Open breaker -
Tests:
- State machine unit tests: Closed → Open → Half-Open → Closed transitions
- Persistence round-trip: event written to memfs, read back, state restored
- TUI: ensure circuit-breaker events render correctly in the cockpit panel and halt header
Phase 3: Integration with Existing Systems
- Ledgers: The drift_log, commitments, and patterns ledgers are natural inputs to circuit-breaker evaluation. Aster's observations that go into ledgers should also be evaluated for circuit-breaker severity.
- N+25 Reflection: Reflection reports that detect identity drift should be considered circuit-breaker candidates.
- CronSensor: If a scheduled heartbeat turn discovers the circuit breaker is Open, it should not run — or should run in audit-only mode.
Phase 4: Not in Scope (Future)
- Automatic circuit-breaker threshold tuning (ML on past events)
- Distributed circuit breaker across federated instances
- Circuit-breaker events as SensorEvents on the EventBus
- Half-Open auto-verification (Aster re-evaluates without human action after timeout)
Files to Modify
| File | Change |
|---|---|
src/core/subconscious/circuit_breaker.rs |
NEW — State machine, BreakerEvent, persistence |
src/core/subconscious/mod.rs |
Add pub mod circuit_breaker |
src/server/consciousness_engine.rs |
Wire evaluation after Aster's analysis pass |
src/backend/local.rs |
Check breaker state at turn start; early return if Open |
src/backend/mod.rs |
Add BackendEvent::CircuitBreaker variant |
src/ui/chat.rs |
Render circuit-breaker state in header |
src/ui/cockpit_panel.rs |
Add CircuitBreaker entry type |
src/cli/commands.rs |
Add souveraine circuit-breaker subcommand |
src/core/config.rs |
Add circuit-breaker config section (thresholds, auto-reset timeout) |
src/api/mod.rs |
Add circuit-breaker endpoints for remote inspection |
docs/ARCHITECTURE_v3.md |
Re-read for original circuit-breaker design intent |
reference/ASTER_ARCHITECTURE.md |
Re-read original Aster circuit-breaker behavior |
Research Needed
- Read
reference/ASTER_ARCHITECTURE.mdin full — understand what the original Aster circuit breaker did (trigger conditions, halt behavior, recovery path) - Read
reference/Fimeg.md— look for "circuit breaker", "halt", "error chain" references in the production system - Read
docs/CONTEXT_CONSTITUTION.mdArticle I — identity boundary language that defines what "drift" means constitutionally - Read
src/server/consciousness_engine.rs— understand the existing Aster observation pipeline end-to-end (what data flows, where it branches) - Read
src/backend/local.rsrun_turn()— understand the turn loop structure to find where a circuit-breaker early-return would hook in - Look at the turn-as-stream-of-BackendEvents model — how
mpsc::channelis consumed by the TUI vs. CLI. An Open breaker needs different behavior per consumer.
Dependencies
- Blocked on: None — Aster's tool loop and observation pipeline are fully implemented
- Heavy interaction with: ledgers (drift_log, commitments, patterns),
ConsciousnessEvent::Surfacingescalation, TurnInjector (should scheduled turns skip if Open?)