Wardenby Bitmill
Documentation

The 4-Engine Model

Warden’s codebase is organized into four engines, each with a distinct responsibility and personality:

EngineRoleMnemonic
ReflexImmediate safety decisions”Reflex acts”
AnchorSession state and drift prevention”Anchor stabilizes”
DreamBackground learning and cross-session memory”Dream learns”
HarborIntegration with assistants, tools, and protocols”Harbor connects”

This is not a marketing taxonomy. The engine boundaries are enforced in the directory structure, the module system, and the API contracts between components.


Why Engines Exist

Early versions of Warden were a flat collection of hook scripts. A pretool_bash.sh script did pattern matching, loop detection, trust calculation, and output formatting in a single file. This worked until it didn’t — around 40 patterns and 3 signal types, the scripts became unmaintainable.

Engines solve three problems:

  1. Separation of concerns. Reflex never touches session state. Anchor never matches patterns. Dream never formats output. Each engine owns its data and its logic.

  2. Independent evolution. Adding a new safety pattern to Reflex requires no changes to Anchor. Adding a new phase to Compass requires no changes to Dream. Engines can be versioned and tested independently.

  3. Budget isolation. Each engine has its own time and space budget. Reflex gets 50ms. Anchor gets 20ms. Dream runs on a background thread with no latency constraint. If one engine is slow, it does not steal budget from another.

How a Hook Call Flows Through Engines

When an assistant invokes a tool (say, Bash with command rm -rf node_modules), the following sequence occurs:

Assistant → Hook Script → Warden Binary → Router

                                            ├─→ Reflex (pattern match, loop check, injection scan)
                                            │     └─→ Verdict: Approve / Deny / Modify

                                            ├─→ Anchor (phase detect, focus update, trust recalc)
                                            │     └─→ Signals: [PhaseShift, FocusDrift, TrustDrop]

                                            ├─→ Dream (async log, no blocking)
                                            │     └─→ (queued for background processing)

                                            └─→ Harbor (format verdict + signals for assistant)
                                                  └─→ Hook Response (JSON)

The critical path is Reflex → Harbor. If Reflex denies, Harbor formats the denial and returns immediately. Anchor and Dream still process the call for state tracking, but they cannot override a Reflex denial.

If Reflex approves, Anchor’s signals are passed to Harbor for optional context injection. The decision to inject depends on the signal severity and the session’s current injection budget.

The Signal Abstraction

Engines communicate through Signals — typed structs that carry a category, severity, and payload:

pub struct Signal {
    pub category: SignalCategory,
    pub severity: Severity,    // Info, Warning, Critical
    pub message: String,
    pub source: &'static str,  // module that generated it
}

Signal categories include:

  • Safety — from Reflex: pattern matches, injection attempts
  • Loop — from Reflex: repetition detection, spiral detection
  • Phase — from Anchor: phase transitions, phase conflicts
  • Focus — from Anchor: drift detection, file-set changes
  • Trust — from Anchor: trust score changes, gate transitions
  • Learn — from Dream: new artifacts, pattern applications
  • Integration — from Harbor: assistant detection, protocol events

Signals flow one direction: from engines to Harbor. Engines do not read each other’s signals directly. If Anchor needs to know about a Reflex denial, it reads the Verdict, not a Signal.

The Verdict Enum

Every hook invocation produces exactly one Verdict:

pub enum Verdict {
    Approve,
    Deny { reason: String, pattern: Option<String> },
    Modify { transformed_input: String, reason: String },
    Skip,  // module has nothing to say
}

Verdicts combine with a simple precedence rule: Deny > Modify > Approve > Skip. If any module returns Deny, the final verdict is Deny regardless of what other modules returned.

Modify is currently used only by the redirect module (rewriting tool calls to preferred alternatives). It cannot override a Deny.

The Budget Struct

Every engine receives a Budget that constrains its execution:

pub struct Budget {
    pub max_duration_ms: u64,
    pub max_output_bytes: usize,
    pub max_injections: u8,
    pub max_signals: u8,
}

Default budgets:

EngineDurationOutputInjectionsSignals
Reflex50ms512B010
Anchor20ms1KB520
Dreamunbounded4KB05
Harbor10ms2KB

If an engine exceeds its duration budget, its processing is interrupted and the last valid state is used. This has never triggered in production (Reflex typically completes in 2-4ms), but the guard exists for pathological inputs.

Engine Directory Tree

The engine model maps directly to the source tree:

src/engines/
├── reflex/
│   ├── mod.rs          # engine entry point
│   ├── sentinel.rs     # pattern matching (~300 patterns)
│   ├── loopbreaker.rs  # repetition and spiral detection
│   ├── tripwire.rs     # injection and bypass detection
│   └── gatekeeper.rs   # central verdict aggregation (future)
├── anchor/
│   ├── mod.rs          # engine entry point
│   ├── compass.rs      # 5-phase session tracking
│   ├── focus.rs        # file-set coherence (0-100)
│   ├── ledger.rs       # turn counting and verification gaps
│   ├── debt.rs         # verification debt tracking
│   └── trust.rs        # trust score calculation
├── dream/
│   ├── mod.rs          # engine entry point
│   ├── worker.rs       # background thread (30s sleep cycle)
│   ├── artifacts.rs    # V2 artifact types
│   └── budget.rs       # cross-session storage limits
└── harbor/
    ├── mod.rs          # engine entry point
    ├── assistant.rs    # trait + adapter implementations
    ├── mcp.rs          # MCP tool definitions
    ├── cli.rs          # CLI command handlers
    └── bridge.rs       # future: external integrations

Each engine’s mod.rs exposes a single public function: pub fn process(call: &ToolCall, state: &mut SessionState, budget: &Budget) -> EngineResult. The router calls these in order and aggregates the results.


Further Reading

  • Reflex Engine — pattern matching, loop detection, injection scanning
  • Anchor Engine — phase tracking, trust scoring, focus management
  • Dream Engine — background learning, cross-session artifacts
  • Harbor Engine — assistant adapters, MCP tools, CLI commands