diff --git a/README.md b/README.md
index 3804abf..51cfbf7 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@
Core libraries for the Dirigent agent orchestration platform.
+> [!CAUTION]
> **Alpha software.** Dirigent is in early active development and not fully battle-tested. Most crates are experimental — APIs will change without notice. There is nothing to install from this repository yet. The standalone tools listed below have their own repositories and maturity levels.
---
@@ -79,6 +80,7 @@ dirigent_core = { git = "https://git.g4b.org/dirigence/dirigent", path = "cr
Replace the crate name and path with the one you need. All crates follow the same pattern.
+> [!CAUTION]
> **Expect breakage.** These are internal library crates under active development. Pin to a specific commit if you depend on stability.
---
diff --git a/crates/dirigent_acp_api/CLAUDE.md b/crates/dirigent_acp_api/CLAUDE.md
deleted file mode 100644
index a57b19b..0000000
--- a/crates/dirigent_acp_api/CLAUDE.md
+++ /dev/null
@@ -1,124 +0,0 @@
-# Package: dirigent_acp_api
-
-ACP Server implementation for accepting incoming ACP connections from external agents.
-
-## Quick Facts
-- **Type**: Library
-- **Main Entry**: src/lib.rs
-- **Dependencies**: axum, tokio, serde, tracing, uuid, async-trait, dirigent_protocol
-- **Status**: Core structure complete, integration with CoreRuntime pending
-
-## Overview
-
-The `dirigent_acp_api` package implements an ACP (Agent-Client Protocol) server that allows Dirigent to accept incoming connections from external ACP clients like Claude Code or custom agents. This enables session sharing, remote orchestration, and multi-client collaboration.
-
-## Architecture
-
-### Core Components
-
-- **config.rs** - Server configuration types (`AcpServerConfig`)
-- **error.rs** - Error types (`AcpServerError`, `JsonRpcErrorObject`)
-- **jsonrpc.rs** - JSON-RPC 2.0 types and parsing
-- **rpc.rs** - RPC handler and method dispatch
-- **session_manager.rs** - Session/client tracking (TODO)
-- **sse.rs** - SSE notification system (TODO)
-- **event_bridge.rs** - Event translation (TODO)
-
-### Key Types
-
-```rust
-pub struct AcpServerConfig {
- pub enabled: bool, // Enable/disable server
- pub port: u16, // Listen port (default: 3001)
- pub allowed_origins: Option>, // CORS origins
- pub max_connections: usize, // Connection limit (default: 100)
-}
-```
-
-### ConnectorOperations Trait
-
-The RPC handler uses a trait abstraction to avoid circular dependencies with dirigent_core:
-
-```rust
-#[async_trait]
-pub trait ConnectorOperations: Send + Sync {
- async fn create_session(&self, connector_id: &str) -> Result;
- async fn load_session(&self, connector_id: &str, session_id: &str) -> Result;
- async fn send_prompt(&self, connector_id: &str, session_id: &str, prompt: &str) -> Result;
- // ... more methods
-}
-```
-
-## API Endpoints
-
-### POST `/rpc`
-
-JSON-RPC 2.0 endpoint supporting:
-- `initialize` - Client handshake
-- `session/new` - Create session
-- `session/load` - Load existing session
-- `session/prompt` - Send prompt
-- `session/cancel` - Cancel generation
-- `session/close` - Close session
-
-### GET `/events`
-
-Server-Sent Events for streaming notifications:
-- `acp/messageChunk` - Streaming content
-- `acp/messageComplete` - Generation complete
-- `acp/sessionIdle` - Ready for input
-
-### GET `/health`
-
-Health check endpoint.
-
-## Configuration UI
-
-The ACP Server is configured via the web UI at **Configuration > ACP Server**:
-
-- Enable/disable toggle
-- Port configuration
-- Max connections limit
-- Allowed origins (CORS)
-- Default connector selection
-- Connected clients management
-
-Server functions in `crates/api/src/acp_server.rs` bridge the UI and this package.
-
-## Implementation Status
-
-**Completed:**
-- Configuration types (`AcpServerConfig`)
-- Error types (`AcpServerError`)
-- JSON-RPC types and parsing
-- RPC handler structure with ConnectorOperations trait
-
-**Pending:**
-- Session Manager implementation
-- SSE Notifier implementation
-- Event Bridge implementation
-- Axum router integration
-- Web server integration
-
-## Key Files
-
-| File | Description |
-|------|-------------|
-| `src/lib.rs` | Module exports and router creation |
-| `src/config.rs` | AcpServerConfig with validation |
-| `src/error.rs` | Error types and codes |
-| `src/jsonrpc.rs` | JSON-RPC 2.0 implementation |
-| `src/rpc.rs` | RPC handler and method dispatch |
-
-## Related Packages
-
-- **dirigent_core** - Provides CoreHandle implementation of ConnectorOperations
-- **dirigent_protocol** - Shared event and message types
-- **api** - Server functions for UI configuration
-- **web** - Configuration UI components
-
-## Documentation
-
-- **Architecture**: `docs/architecture/acp_server.md`
-- **Configuration**: `docs/configuration/acp-connectors.md`
-- **Tasks**: `docs/building/07_acp_serve/02_acp_server_tasks.md`
diff --git a/crates/dirigent_config/CLAUDE.md b/crates/dirigent_config/CLAUDE.md
deleted file mode 100644
index 4a1958f..0000000
--- a/crates/dirigent_config/CLAUDE.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# dirigent_config
-
-Platform-native configuration and data path resolution.
-
-## Purpose
-Provides `DirigentPaths` for resolving config/data directories across Linux, macOS, Windows.
-Creates a symlink on Linux/macOS from config_dir/data -> data_dir for discoverability.
-
-## Key Types
-- `DirigentPaths` -- resolved config_dir + data_dir with convenience methods
-- `ConfigPathError` -- error enum for path resolution failures
-
-## Usage
-```rust
-let paths = DirigentPaths::resolve()?;
-paths.ensure_dirs()?; // creates dirs + symlink
-let config = paths.config_file(); // ~/.config/dirigent/dirigent.toml
-```
-
-## Dependencies
-- `dirs` -- cross-platform directory resolution
-- Zero UI dependency -- used by core, archivist, zed crates
diff --git a/crates/dirigent_core/CLAUDE.md b/crates/dirigent_core/CLAUDE.md
deleted file mode 100644
index 855b66c..0000000
--- a/crates/dirigent_core/CLAUDE.md
+++ /dev/null
@@ -1,616 +0,0 @@
-# Package: dirigent_core
-
-Core orchestration engine for multi-connector agent system management.
-
-## Quick Facts
-- **Type**: Library
-- **Main Entry**: src/lib.rs
-- **Dependencies**: dirigent_protocol, tokio, axum, serde, uuid
-
-## Architecture Overview
-
-The dirigent_core package provides a **runtime-based architecture** for managing long-lived connections to external agent systems (OpenCode.ai, ACP agents, etc.). The core abstraction is the **Connector**, which represents a bidirectional communication channel to an agent system.
-
-### Core Components
-
-#### CoreRuntime
-The central orchestrator for managing connectors. It maintains:
-- Registry of active connectors (keyed by ConnectorId)
-- Global event broadcast channel for system-wide events
-- User registry for ownership and authorization
-- Configuration state (with persistence support)
-
-#### CoreHandle
-Lightweight, cloneable wrapper around CoreRuntime. Uses Arc internally for cheap cloning across async tasks and server functions.
-
-#### Connector Trait
-Defines the interface for connector implementations:
-- Command channel (mpsc) for control operations
-- Event broadcast channel for publishing events
-- State tracking (Initializing, Connecting, Ready, Error, Stopped)
-- User ownership for authorization
-
-#### ConnectorHandle
-Concrete implementation of the Connector trait that wraps:
-- Metadata (id, kind, owner, title)
-- Shared state (protected by RwLock)
-- Command and event channels
-- Optional task handle for lifecycle management
-
-### Connector Implementations
-
-#### OpenCodeConnector
-Connector for OpenCode.ai REST + SSE API:
-- HTTP client for session/message operations
-- SSE event stream for real-time updates
-- Background task loop for command processing
-- State machine for connection lifecycle
-- **TurnComplete emission**: Uses `TurnCompleteTrigger::ExplicitSignal` after `MessageCompleted` events (based on upstream session.idle signals)
-
-#### AcpConnector (Future)
-Connector for Agent-Client Protocol:
-- WebSocket or HTTP/2 transport
-- ACP message protocol handling
-- Tool execution and streaming support
-
-## Key Files
-
-### Core Runtime
-- `src/runtime.rs` - CoreRuntime and CoreHandle implementation
-- `src/types.rs` - Core types (ConnectorId, ConnectorState, User, etc.)
-- `src/error.rs` - Error types for the runtime
-- `src/config.rs` - Configuration types and template system
-
-### Connectors
-- `src/connectors/mod.rs` - Connector trait and ConnectorHandle
-- `src/connectors/opencode/mod.rs` - OpenCode connector implementation
-- `src/connectors/opencode/config.rs` - OpenCode-specific configuration
-- `src/connectors/acp/mod.rs` - ACP connector implementation (in progress)
-
-### ACP Protocol Implementation
-- `src/acp/protocol/initialize.rs` - Protocol initialization and capability negotiation
-- `src/acp/protocol/authenticate.rs` - Optional authentication flow
-- `src/acp/protocol/session.rs` - Session lifecycle (new, load, set_mode, cancel)
-- `src/acp/protocol/prompt.rs` - Prompt requests with content blocks (Phases 3-7 complete)
-- `src/acp/protocol/streaming.rs` - Session update notifications and handlers
-- `src/acp/protocol/stop_reason.rs` - Stop reason interpretation and actions
-- `src/acp/protocol/cancellation.rs` - Cancellation and disconnect handling
-- `src/acp/protocol/error.rs` - Error classification and retry logic
-- `src/acp/connector_state.rs` - Connection and session state management
-- `src/acp/transport/mod.rs` - Transport abstraction layer
-- `src/acp/transport/stdio.rs` - Stdio transport (process spawning)
-- `src/acp/transport/http.rs` - HTTP+SSE transport
-
-### Bidirectional Request Handling Pattern
-
-The ACP connector implements **true bidirectional communication** where both client and agent can send JSON-RPC requests at any time. This creates an architectural challenge: how to maintain synchronous request/response semantics (async/await) while handling incoming agent requests during outgoing client requests.
-
-**The Challenge:**
-
-When the connector sends `session/prompt` to the agent:
-1. It calls `send_request()` which awaits the JSON-RPC response
-2. The agent may send permission requests (e.g., `tools/write`) before responding
-3. These agent requests arrive as `ConnectorCommand::AgentResponse` via the command channel
-4. If `send_request()` only polls transport and response channels, the command channel isn't polled
-5. **Result**: Deadlock - agent waits for permission → permission stuck in channel → client waits for prompt response
-
-**The Solution (src/connectors/acp/connector.rs:1253-1523):**
-
-The `send_request()` method uses `tokio::select!` to poll **three** sources simultaneously:
-- **Response channel** (`response_rx`) - Waiting for the correlated JSON-RPC response
-- **Transport channel** - Receiving messages/notifications from agent (may trigger response)
-- **Command channel** (`cmd_rx`) - Receiving commands from event bridge (e.g., `AgentResponse`)
-
-When an `AgentResponse` command arrives during `send_request()`:
-1. Extract the response payload from the command
-2. Send it to the agent via transport immediately
-3. Remove from pending requests map
-4. Continue waiting for the original prompt response
-
-This pattern is **idiomatic async Rust** for implementing synchronous abstractions over bidirectional transports. It's similar to:
-- gRPC bidirectional streaming with request/response correlation
-- WebSocket clients with RPC-style method calls
-- HTTP/2 multiplexing with concurrent streams
-
-**Why Not Separate Tasks?**
-
-Alternative architectures (separate command processing task, full actor model) introduce:
-- Complex synchronization between tasks
-- Race conditions on shared state
-- Message ordering guarantees across channels
-- Significantly more code and cognitive overhead
-
-The single-task, multi-channel select pattern keeps all state local and eliminates these issues.
-
-**Key Invariant:**
-
-Any method that blocks waiting for a response MUST also poll the command channel to process `AgentResponse` commands, otherwise bidirectional flows deadlock.
-
-## Main Exports
-
-### Runtime
-- `CoreRuntime` - Main orchestrator
-- `CoreHandle` - Cloneable runtime handle
-- `CoreConfig` - Runtime configuration
-
-### Types
-- `ConnectorId`, `UserId` - Type aliases for IDs
-- `ConnectorKind` - Enum of connector types (OpenCode, Acp, Mock)
-- `ConnectorState` - Lifecycle state enum
-- `ConnectorSummary` - Lightweight connector view
-- `User` - User information
-
-### Connectors
-- `Connector` - Trait for connector implementations
-- `ConnectorHandle` - Handle to a running connector
-- `ConnectorCommand` - Commands sent to connectors
-- `OpenCodeConnector` - OpenCode.ai integration
-- `OpenCodeConfig` - OpenCode connector configuration
-
-### Configuration
-- `ConnectorConfig` - Configuration for creating connectors
-- `apply_template()` - Apply connector templates with patches
-
-### ACP Protocol Types (Phases 3-7)
-- **Prompt Turn**:
- - `SessionPromptRequest`, `SessionPromptResponse` - Prompt requests/responses
- - `ContentBlock` - Text, Image, Audio, Resource, ResourceLink content
- - `EmbeddedResource` - Text or Blob embedded resources
- - `StopReason` - EndTurn, MaxTokens, MaxTurnRequests, Refusal, Cancelled
- - `PromptError` - Timeout, JsonRpcError, TransportError, Cancelled, ValidationError
-
-- **Streaming Updates**:
- - `SessionUpdate` - All update types (agent_message_chunk, tool_call, plan, etc.)
- - `SessionUpdateNotification` - Notification wrapper with session_id
- - `ToolCallInfo` - Tool call tracking with status and content
- - `ToolKind` - Read, Edit, Search, Execute, Think, Other
- - `ToolCallStatus` - Pending, InProgress, Completed, Failed, Cancelled
- - `ToolCallContent` - Content, Diff, Terminal output
- - `MessageAccumulator` - Message chunk accumulation helper
- - `PlanEntry`, `Command` - Plan and command structures
-
-- **Stop Reason Handling**:
- - `StopReasonAction` - Complete, ShowWarning, ShowError, ShowInfo
- - `handle_stop_reason()` - Interpret stop reasons
- - `is_continuable()`, `is_error()` - Stop reason classification
-
-- **Cancellation**:
- - `handle_cancellation()` - Cancel pending operations
- - `cancel_pending_tool_calls()` - Mark tool calls as cancelled
- - `handle_disconnect()` - Handle transport disconnect
-
-- **Error Classification**:
- - `ClassifiedError` - Error with class, message, details, retry_after
- - `ErrorClass` - Transient, Terminal, User
- - `ErrorSeverity` - Info, Warning, Error, Fatal
- - `ErrorAction` - Retry, Reconnect, CheckConfig, ContactSupport, Dismiss
- - `classify_jsonrpc_error()`, `classify_transport_error()` - Classify errors
- - `exponential_backoff()` - Calculate retry delays
-
-## Usage Examples
-
-### Creating a Runtime
-
-```rust
-use dirigent_core::{CoreRuntime, CoreConfig, CoreHandle};
-
-// Load config from file or use default
-let config = CoreConfig::load_config(None)?;
-
-// Create runtime
-let runtime = CoreRuntime::new(config);
-
-// Wrap in handle for cheap cloning
-let handle = CoreHandle::new(runtime);
-```
-
-### Creating a Connector
-
-```rust
-use dirigent_core::{ConnectorConfig, ConnectorKind, OpenCodeConfig};
-use serde_json::json;
-
-// Build OpenCode connector config
-let config = OpenCodeConfig {
- base_url: "http://localhost:12225".to_string(),
- title: "My OpenCode".to_string(),
- initial_session: None,
-};
-
-// Serialize to JSON for ConnectorConfig
-let params = serde_json::to_value(&config)?;
-
-let connector_config = ConnectorConfig {
- id: None, // Runtime generates ID
- kind: ConnectorKind::OpenCode,
- owner: Some("user-123".to_string()),
- title: Some("My OpenCode".to_string()),
- params,
-};
-
-// Create connector via runtime
-let connector_id = handle.create_connector(
- "user-123".to_string(),
- connector_config
-).await?;
-```
-
-### Using Templates
-
-```rust
-use dirigent_core::{apply_template, ConnectorKind};
-use serde_json::json;
-
-// Use default template with custom URL
-let connector_config = apply_template(
- ConnectorKind::OpenCode,
- "default",
- json!({
- "base_url": "http://localhost:8080",
- "title": "Custom OpenCode"
- })
-)?;
-
-let connector_id = handle.create_connector(
- "user-123".to_string(),
- connector_config
-).await?;
-```
-
-### Managing Connectors
-
-```rust
-// List all connectors
-let all_connectors = handle.list_connectors(None).await;
-
-// List connectors for a specific user
-let user_connectors = handle.list_connectors(Some("user-123".to_string())).await;
-
-// Get a specific connector
-let connector = handle.get_connector(&connector_id).await;
-
-// Stop a connector
-handle.stop_connector(&connector_id).await?;
-
-// Restart a stopped connector
-handle.restart_connector(&connector_id).await?;
-
-// Remove a connector
-handle.remove_connector(&connector_id).await?;
-```
-
-### Connector Lifecycle with Restart
-
-Connectors can be restarted after being stopped or entering an error state:
-
-```rust
-// Create and start a connector
-let connector_id = handle.create_connector(
- "user-123".to_string(),
- connector_config
-).await?;
-// Connector is now in Ready state
-
-// Stop the connector
-handle.stop_connector(&connector_id).await?;
-// Connector is now in Stopped state
-
-// Restart the connector (recreates background task with fresh channels)
-handle.restart_connector(&connector_id).await?;
-// Connector transitions: Stopped → Initializing → Connecting → Ready
-
-// Restart preserves:
-// - Connector ID (same instance)
-// - Configuration (base_url, title, etc.)
-// - Event broadcast channel (subscribers continue receiving)
-// - State Arc (observers see real-time updates)
-
-// Restart recreates:
-// - Command channel (new sender/receiver pair)
-// - Connector instance (fresh OpenCodeConnector)
-// - Background task (new spawn)
-// - Task handle (new JoinHandle)
-```
-
-### Sending Commands
-
-```rust
-use dirigent_core::connectors::ConnectorCommand;
-
-// Get connector handle
-let connector = handle.get_connector(&connector_id).await.unwrap();
-
-// Subscribe to events
-let mut events = connector.subscribe();
-
-// Send a command
-let cmd_tx = connector.command_tx();
-cmd_tx.send(ConnectorCommand::ListSessions).await?;
-
-// Receive events
-while let Ok(event) = events.recv().await {
- match event {
- Event::SessionsListed { sessions } => {
- println!("Got {} sessions", sessions.len());
- break;
- }
- Event::Error { message } => {
- eprintln!("Error: {}", message);
- break;
- }
- _ => {}
- }
-}
-```
-
-### Global Event Stream
-
-```rust
-// Subscribe to every event on the SharingBus. Callers can also pick an
-// `EventFilter` via `subscribe_filtered()` to receive only the events
-// they care about.
-let mut bus_rx = handle.sharing_bus().subscribe_all().await;
-
-tokio::spawn(async move {
- while let Some(bus_event) = bus_rx.rx.recv().await {
- println!("Bus event: {:?}", bus_event.event);
- }
-});
-```
-
-## Configuration
-
-### Runtime Configuration (dirigent.toml or dirigent.json)
-
-```toml
-port = 3000
-project_dir = "."
-project_name = "my_project"
-templates_enabled = true
-
-[[connectors]]
-id = "opencode-1"
-kind = "OpenCode"
-owner = "user-123"
-title = "OpenCode Local"
-
-[connectors.params]
-base_url = "http://localhost:12225"
-title = "OpenCode Local"
-initial_session = null
-```
-
-### Templates
-
-Available templates:
-- `opencode/default` - Standard localhost OpenCode connector
-- `acp/claude-default` - Claude API connector (stub for future)
-
-## Connector Event Emission Patterns
-
-### TurnComplete Event Semantics
-
-All connectors emit `Event::TurnComplete` to signal that a turn/message is finalized. This is the **primary signal** for:
-- Archivist to finalize and write message to disk
-- UI cache to lock message state as immutable
-- Conductor bridge to flush response to upstream
-
-**Event ordering guarantee**:
-```text
-MessageCompleted → TurnComplete → SessionIdle
-```
-
-### Connector-Specific TurnComplete Strategies
-
-Different connectors use different strategies to determine when a turn is complete:
-
-#### OpenCode Connector
-- **Trigger**: `TurnCompleteTrigger::ExplicitSignal`
-- **Strategy**: Relies on upstream `session.idle` events from OpenCode.ai
-- **Implementation**: After translating `MessageCompleted`, emits `TurnComplete` then `SessionIdle`
-- **Code location**: `crates/dirigent_core/src/connectors/opencode.rs:550-575`
-
-#### ACP Connector (stdio transport)
-- **Trigger**: `TurnCompleteTrigger::ResponseReceived`
-- **Strategy**: JSON-RPC response message is the final message in a turn
-- **Implementation**: Emits `TurnComplete` after receiving the JSON-RPC response to `session/prompt`
-- **Code location**: `crates/dirigent_core/src/connectors/acp/connector.rs:328`
-
-#### Gateway Connector
-- **Trigger**: `TurnCompleteTrigger::OperationsComplete`
-- **Strategy**: Tracks pending tool calls and emits when all operations resolve
-- **Implementation**: Monitors tool call status changes and emits when last pending call completes
-- **Code location**: `crates/dirigent_core/src/connectors/gateway/mod.rs:464,556,626,678`
-
-**Important**: Connectors MUST emit `TurnComplete` exactly once per turn. Duplicate emissions can cause archiving issues and UI state corruption.
-
-### Gateway Session Transfer Mechanics
-
-The Gateway connector serves as an **entry point** for incoming ACP connections. Sessions can be transferred to real agent connectors (Claude, etc.) via `/select-connector` commands.
-
-#### Key Principle: New Connector Is Authority
-
-**After transfer, the target connector becomes the sole authority for session configuration.**
-
-```text
-Before transfer:
- Gateway has placeholder modes: "ask", "write", "yolo"
- Gateway has placeholder models: "simple", "default", "high"
-
-After transfer to Claude:
- Claude's actual modes/models become authoritative
- Gateway's placeholders are irrelevant
- Editor receives config_option_update with Claude's real options
-```
-
-#### Transfer Flow
-
-1. User sends `/select-connector claude` in Gateway session
-2. Gateway emits `SessionTransferRequest` to CoreRuntime
-3. CoreRuntime creates/loads session in target connector
-4. Target connector emits `SessionCreated` with its modes/models
-5. CoreRuntime extracts modes/models from `SessionCreated` event
-6. CoreRuntime emits `SessionTransferred` event with modes/models
-7. Event bridge sends `config_option_update` to editor with target's modes/models
-
-#### What Does NOT Happen
-
-- Gateway does NOT adjust or map its values to the target connector
-- Gateway does NOT remain involved after transfer completes
-- Target connector does NOT inherit Gateway's mode/model selections
-- No "mapping" between Gateway placeholders and real connector values
-
-#### Code Locations
-
-- Transfer request handling: `src/runtime.rs:execute_transfer()`
-- Gateway commands: `src/connectors/gateway/commands.rs`
-- SessionTransferred event: `dirigent_protocol/src/events/mod.rs`
-- config_option_update emission: `dirigent_acp_api/src/event_bridge.rs:handle_session_transferred_internal()`
-
-## Architecture Patterns
-
-### Request-Response Pattern
-For operations that need results (list_sessions, list_messages):
-1. Subscribe to connector events
-2. Send command via command channel
-3. Wait for corresponding response event (with timeout)
-4. Return result or error
-
-### Fire-and-Forget Pattern
-For operations that stream results (send_message):
-1. Send command via command channel
-2. Return immediately
-3. Clients subscribe to event stream for updates
-
-### Lifecycle Management
-Connectors progress through states:
-1. **Initializing** - Created but not connecting
-2. **Connecting** - Attempting to establish connection
-3. **Ready** - Connected and operational
-4. **Error** - Encountered failure (with error message)
-5. **Stopped** - Shutdown or unrecoverable error
-
-#### Restart Support
-Connectors in `Stopped` or `Error` state can be restarted:
-- **restart_connector()** recreates the connector's background task with fresh channels
-- Preserves connector identity (ID, owner, configuration)
-- Preserves event broadcast channel (existing subscribers continue receiving)
-- Recreates command channel and background task
-- State transitions: `Stopped`/`Error` → `Initializing` → `Connecting` → `Ready`
-
-### Configuration Persistence
-The runtime automatically saves configuration to disk when:
-- A connector is created
-- A connector is removed
-- Configuration is explicitly saved
-
-This ensures connectors are restored on server restart.
-
-## Session Tracking Responsibilities
-
-**IMPORTANT**: CoreRuntime is a **stateless orchestrator** for session operations. It does NOT maintain session state or cache message history.
-
-### What CoreRuntime Does
-
-- **Route Commands**: Forward session/message commands to appropriate connectors
-- **Broadcast Events**: Relay connector events to global event stream
-- **Manage Connectors**: Track which connectors are active and available
-- **Persist Configuration**: Save/load connector configuration (not session data)
-
-### What CoreRuntime Does NOT Do
-
-- **Cache Sessions**: Does not maintain lists of sessions or session metadata
-- **Store Messages**: Does not retain message content or history
-- **Buffer Events**: Does not cache events for replay or historical access
-- **Track Session State**: Does not know which sessions exist or their current state
-
-### Stateless Design Rationale
-
-1. **Multi-Connector Support**: With multiple connectors (OpenCode, ACP, etc.), caching sessions would require complex invalidation
-2. **Memory Efficiency**: Long-running server should not accumulate unbounded session history
-3. **Single Source of Truth**: External APIs (OpenCode.ai, ACP agents) are authoritative for session state
-4. **Scalability**: Stateless design supports future horizontal scaling
-
-### Data Flow for Session Operations
-
-**List Sessions**:
-```
-Server Function → CoreRuntime.get_connector() → Send Command → Connector queries API
- ↓ ↓
- Returns handle Broadcasts SessionsListed
- ↓
- Server function returns to UI
- (CoreRuntime does NOT cache list)
-```
-
-**Send Message**:
-```
-Server Function → CoreRuntime.get_connector() → Send Command → Connector sends to API
- ↓ ↓
- Returns handle Broadcasts MessageSent
- ↓
- SSE pushes to UI in real-time
- (CoreRuntime does NOT store message)
-```
-
-All session data passes through CoreRuntime but is **never cached**. See `docs/architecture/session_tracking.md` for the complete three-layer architecture (CoreRuntime, UI Cache, Archivist).
-
-## Phase 4: `SharingBus` + `StreamRegistry` (2026-04-21)
-
-Every `Event` emitted by a connector or the runtime is published onto a
-single `SharingBus` that owns:
-
-- A `broadcast::Sender` as the internal multicast.
-- A worker task that receives from that broadcast and dispatches per-
- subscriber `mpsc::Sender` pipes with filter matching.
-- A `HashMap<(connector_id, native_session_id), scroll_id>` cache that
- late-binds `routing.scroll_id` for events emitted before their
- `SessionRegistered` event arrived.
-
-### Subscriber model
-
-`SharingBus::subscribe_all()` and `subscribe_filtered(EventFilter, cap)`
-return a `BusReceiver { id, rx, lagged }`. Filters are applied by the
-worker — subscribers never allocate a closure for skipped events.
-
-`EventFilter` variants: `All | ScrollId | ConnectorUid | Kinds | AnyOf
-| AllOf`.
-
-### Stream registry
-
-The runtime owns a `StreamRegistry` and a `StreamFactoryRegistry`.
-Streams are attached via `CoreRuntime::attach_stream(StreamConfig)`:
-
-- Factory registry resolves the `kind` string → concrete
- `Arc`.
-- The new stream gets its own `BusReceiver` scoped via `scope_to_filter`
- (Session → ScrollId, Connector → ConnectorUid, ArchiveWide → All).
-- A worker task pumps events into `stream.on_event(&bus_event).await`.
-
-Health drift: `record_failure` / `record_success` in `sharing/health.rs`
-transitions `HealthStatus` on 5 consecutive failures
-(`Healthy → Degraded → Unavailable`).
-
-### Replay
-
-`CoreRuntime::replay_session_to_stream(scroll_id, stream_id, opts)`
-loads archived messages via the archivist's read API and calls
-`stream.on_event` directly, bypassing the bus. Each replayed event has
-`origin = EventOrigin::Replay { replay_id }` and
-`routing.scroll_id = Some(scroll_id)`.
-
-### Config
-
-`[[streams]]` blocks in `dirigent.toml` are parsed into `StreamsConfig`
-and applied at boot (best-effort: failures log + continue).
-
-## Related Packages
-- **api** - Server functions that wrap CoreRuntime operations
-- **web** - Dioxus UI that calls server functions
-- **dirigent_protocol** - Shared event and message types
-- **opencode_client** - Low-level OpenCode.ai HTTP client (used by OpenCodeConnector)
-
-## Documentation
-- README: ./README.md
-- Architecture: ../../docs/architecture/overview.md
-- Migration Guide: ../../docs/migration/singleton_to_runtime.md
diff --git a/crates/dirigent_protocol/CLAUDE.md b/crates/dirigent_protocol/CLAUDE.md
deleted file mode 100644
index 28ae11a..0000000
--- a/crates/dirigent_protocol/CLAUDE.md
+++ /dev/null
@@ -1,268 +0,0 @@
-# dirigent_protocol
-
-**Version:** 0.2.0
-**Status:** Active Development
-
-ACP/MCP-aligned protocol library for agent-client interactions.
-
-## Quick Links
-
-- **Package README**: [README.md](README.md) - Main documentation
-- **Streaming Model**: [docs/streaming_model.md](docs/streaming_model.md) - Detailed SessionUpdate guide
-- **Migration Guide**: [docs/migration_from_0.1.md](docs/migration_from_0.1.md) - Upgrading from 0.1.x
-- **Architecture Doc**: [../../docs/architecture/protocol.md](../../docs/architecture/protocol.md) - System design
-
-## Purpose
-
-This package provides the core event protocol for Dirigent, enabling:
-- Real-time streaming of agent interactions
-- Provider-agnostic event representation
-- Tool lifecycle management
-- Structured content representation
-
-## Key Types
-
-```rust
-use dirigent_protocol::{
- Event, // Top-level event enum
- SessionUpdate, // Streaming content updates
- ContentBlock, // Structured content (Text, ResourceLink)
- ToolCall, // Tool execution state
- ToolCallStatus, // Pending → Running → Completed/Error
- TurnCompleteTrigger, // How turn completion was detected
-};
-```
-
-## Event Semantics: MessageCompleted vs TurnComplete vs SessionIdle
-
-Understanding the distinction between these three events is critical for correct system behavior:
-
-### MessageCompleted - "Metadata is ready"
-- **Purpose**: Informational - signals that message metadata exists
-- **Timing**: Emitted when message record is created, content may still be streaming
-- **Consumer action**: Update UI status indicators ("Assistant is typing" → "Complete")
-- **Example**: Show message timestamp, update message count
-
-### TurnComplete - "All content received" (ACTIONABLE)
-- **Purpose**: **Primary finalization signal** - all content for this turn is complete
-- **Timing**: Emitted AFTER all content chunks, tool calls, and metadata updates
-- **Consumer action**: **Finalize storage, lock state, trigger post-processing**
-- **Example**: Write message to disk, mark as immutable, generate summaries
-
-### SessionIdle - "No recent activity"
-- **Purpose**: Informational - indicates session is quiet
-- **Timing**: Emitted AFTER TurnComplete as final safety signal
-- **Consumer action**: Hide spinners, update activity indicators
-- **Example**: Remove "typing" animation, update last activity timestamp
-
-### Event Ordering Guarantee
-
-```text
-1. MessageStarted (message created)
-2. SessionUpdate::*Chunk (content streaming)
-3. SessionUpdate::ToolCall* (tool execution)
-4. MessageCompleted (metadata ready) ← UI: "Complete"
-5. TurnComplete ← FINALIZE HERE!
-6. SessionIdle ← UI: hide spinner
-```
-
-### Consumer Behavior Table
-
-| Consumer | MessageCompleted | TurnComplete | SessionIdle |
-|----------|------------------|--------------|-------------|
-| **Archivist** | Ignore | **Finalize and write** | Safety net |
-| **UI Cache** | Update status | **Lock state** | Hide spinner |
-| **Conductor Bridge** | - | **Flush response** | Fallback flush |
-
-### TurnCompleteTrigger Variants
-
-The `TurnCompleteTrigger` enum indicates **how** the system determined completion:
-
-- **`ExplicitSignal`**: Upstream provider sent explicit completion (e.g., OpenCode session.idle)
-- **`ResponseReceived`**: JSON-RPC response received (ACP stdio - response is last message)
-- **`OperationsComplete`**: All tracked operations finished (e.g., pending tool calls resolved)
-- **`IdleTimeout { duration_ms }`**: Timeout-based detection (fallback mechanism)
-
-**For most consumers**, treat all triggers the same - the turn is complete. The trigger type is primarily for debugging and observability.
-
-## Usage Pattern
-
-```rust
-use dirigent_protocol::{Event, SessionUpdate, ContentBlock};
-
-fn handle_event(event: Event) {
- match event {
- Event::SessionUpdate { session_id, update } => {
- match update {
- SessionUpdate::AgentMessageChunk { message_id, content, .. } => {
- if let ContentBlock::Text { text } = content {
- println!("Agent: {}", text);
- }
- }
- SessionUpdate::ToolCall { tool_call, .. } => {
- println!("Tool: {}", tool_call.tool_name);
- }
- _ => {}
- }
- }
- _ => {}
- }
-}
-```
-
-## Architecture
-
-```
-dirigent_protocol/
-├── src/
-│ ├── types/ # Core types
-│ │ ├── content.rs # ContentBlock definitions
-│ │ ├── updates.rs # SessionUpdate variants
-│ │ ├── tool.rs # ToolCall, ToolCallStatus
-│ │ └── meta.rs # Provider metadata
-│ ├── session.rs # Session types
-│ ├── conversation.rs # Message types
-│ ├── events.rs # Event enum
-│ └── adapters/ # Provider adapters
-│ ├── opencode.rs # OpenCode translation
-│ └── rest.rs # REST translation
-├── docs/ # Detailed documentation
-├── examples/ # Usage examples
-└── tests/ # Integration tests
-```
-
-## Version 0.2.0 Changes
-
-**Breaking:**
-- Removed `Event::MessagePartAdded`
-
-**New:**
-- `SessionUpdate` event system (ACP-style)
-- `ContentBlock` types (MCP-compatible)
-- `ToolCall` with lifecycle tracking
-- Provider metadata via `_meta`
-
-See [docs/migration_from_0.1.md](docs/migration_from_0.1.md) for migration guide.
-
-## Development
-
-### Running Tests
-```bash
-cargo test --package dirigent_protocol
-```
-
-### Checking Code
-```bash
-cargo check --package dirigent_protocol
-```
-
-### Running Examples
-```bash
-cargo run --package dirigent_protocol --example session_metadata_demo
-```
-
-## Integration
-
-This package is used by:
-- **api** package: Server functions consume protocol events
-- **web** package: UI renders protocol events
-- **dirigent_core** (future): Runtime emits protocol events
-
-## Adapters
-
-The adapter system translates provider-specific events to Dirigent Protocol:
-
-- **OpenCodeAdapter**: Translates OpenCode.ai events
-- **RESTAdapter**: Converts REST API responses
-
-Adapters preserve provider metadata in the `_meta` field for debugging and traceability.
-
-## Current Scope
-
-**Phase 1 (Implemented):**
-- User/Agent/Thought message streaming
-- Tool lifecycle (Pending → Running → Completed/Error)
-- Text and ResourceLink content types
-- Provider metadata support
-
-**Deferred to Future Phases:**
-- Plans and mode switching
-- Permission system
-- Embedded resources (full content)
-- Rich media (images, audio)
-- Multi-agent communication
-
-See [../../docs/building/03_acp_prep/04_first_order_refactor.md](../../docs/building/03_acp_prep/04_first_order_refactor.md) for the full plan.
-
-## Standards Alignment
-
-**Agent-Client Protocol (ACP):**
-- Session-centric streaming
-- Separate content types (user/agent/thought)
-- Tool status tracking
-
-**Model Context Protocol (MCP):**
-- ContentBlock structure
-- Resource links
-- Extensible content types
-
-Differences from standards are documented in [../../docs/architecture/protocol.md](../../docs/architecture/protocol.md).
-
-## Anti-Patterns
-
-### Timeout-Based Event Waiting (FORBIDDEN)
-
-**Never use timeout-based waiting to receive events that should be available immediately.**
-
-```rust
-// ❌ BAD - Race condition waiting for event
-async fn wait_for_metadata_event(
- events: &mut broadcast::Receiver,
- timeout: Duration,
-) -> Option {
- let start = Instant::now();
- while start.elapsed() < timeout {
- match tokio::time::timeout(Duration::from_millis(100), events.recv()).await {
- Ok(Ok(Event::SessionMetadataReceived { .. })) => return Some(...),
- _ => continue,
- }
- }
- None
-}
-```
-
-**Why this is wrong:**
-1. **Race condition**: The event may have been emitted before the receiver subscribed
-2. **Arbitrary delays**: 500ms waits add latency for no good reason
-3. **Silent failures**: Timeout expiring doesn't indicate the real problem
-4. **Fragile**: Works "most of the time" but fails under load or timing variations
-
-**Instead, pass data directly:**
-```rust
-// ✅ GOOD - Extract data from existing events
-async fn create_session_in_connector(...) -> Result<(String, Option, Option), String> {
- // The SessionCreated event already contains models/modes
- match event {
- Event::SessionCreated { session, .. } => {
- Ok((session.id, session.models, session.modes))
- }
- }
-}
-```
-
-**Rule**: If you find yourself writing `timeout(Duration::from_millis(N), events.recv())` to wait for an event that "should" arrive, the architecture is wrong. Refactor to pass data directly through return values or existing event payloads.
-
-## Contributing
-
-When adding features:
-1. Update type definitions in `src/types/`
-2. Add comprehensive tests
-3. Update documentation in `docs/`
-4. Add examples if applicable
-5. Update CHANGELOG.md
-
-## See Also
-
-- Main project: [../../CLAUDE.md](../../CLAUDE.md)
-- Architecture docs: [../../docs/architecture/](../../docs/architecture/)
-- Building docs: [../../docs/building/](../../docs/building/)
diff --git a/crates/dirigent_tools/CLAUDE.md b/crates/dirigent_tools/CLAUDE.md
deleted file mode 100644
index 2abd038..0000000
--- a/crates/dirigent_tools/CLAUDE.md
+++ /dev/null
@@ -1,169 +0,0 @@
-# Package: dirigent_tools
-
-Tool implementations for ACP client operations with sandboxing and security.
-
-## Quick Facts
-- **Type**: Library
-- **Main Entry**: src/lib.rs
-- **Dependencies**: tokio, serde, anyhow, thiserror, tracing, regex, globset, similar, dunce
-- **Status**: Scaffolding phase - structure complete, implementation pending
-
-## Purpose
-
-This package provides the foundational tool operations for the Dirigent ACP client, implementing file operations, terminal execution, and search capabilities with strong security guarantees. It is designed to support ACP-compliant agents by providing safe, sandboxed tool handlers.
-
-## Module Structure
-
-### Core Modules
-- **error** (`src/error.rs`) - Error types for tool operations
-- **config** (`src/config.rs`) - Configuration types (sandbox, permissions, terminal, search, embedding)
-- **path** (`src/path.rs`) - Cross-platform path normalization and containment checking
-
-### Tool Modules
-- **fs** (`src/fs.rs`) - File read/write/edit operations
-- **search** (`src/search.rs`) - Glob/grep/ls search operations
-- **terminal** (`src/terminal.rs`) - Command execution and output capture
-
-### Security Modules
-- **permission** (`src/permission.rs`) - Permission prompt system and decision caching
-- **audit** (`src/audit.rs`) - Structured audit logging
-
-### Integration Modules
-- **tool_call** (`src/tool_call.rs`) - ACP tool call metadata and helpers
-
-## Security Model
-
-All operations are subject to:
-
-1. **Sandbox Containment**: Operations restricted to configured allowed roots
-2. **Blocklist Enforcement**: Sensitive paths explicitly denied
-3. **Permission Prompts**: Write/execute operations require user approval (configurable)
-4. **Resource Limits**: Bounded file sizes, search results, terminal output
-5. **Audit Logging**: All operations logged with structured context
-
-## Platform Support
-
-Windows is a first-class platform with explicit support for:
-- Backslash and forward slash separators
-- Drive letters (C:\, D:\)
-- UNC paths (\\server\share\...)
-- Long path prefixes (\\?\...)
-- MINGW-style paths (/c/Users/...)
-- Junctions and symlinks
-- cmd.exe and PowerShell
-
-All path normalization and containment logic handles these cases.
-
-## Configuration (Future)
-
-Configuration types will be implemented in SCAFF-05:
-
-```rust
-pub struct SandboxConfig {
- pub allowed_roots: Vec,
- pub blocked_paths: Vec, // glob patterns
- pub allow_symlink_escape: bool,
- pub follow_symlinks_within_roots: bool,
- pub read_enabled: bool,
- pub write_enabled: bool,
- pub max_read_bytes: u64,
- pub max_write_bytes: u64,
- // ... more fields
-}
-
-pub struct PermissionConfig {
- pub mode: PermissionMode, // Ask | Whitelist | Yolo
- pub remember_decisions: bool,
- pub remember_ttl_secs: u64,
- pub scope: DecisionScope, // PerConnector | PerSession
- pub whitelist: WhitelistConfig,
-}
-
-pub struct TerminalConfig {
- pub enabled: bool,
- pub default_cwd: PathBuf,
- pub env_allowlist: Vec,
- pub command_blocklist: Vec,
- pub output_byte_limit: u64,
- pub max_runtime_secs: u64,
-}
-
-pub struct SearchConfig {
- pub max_results: u32,
- pub max_bytes: u64,
- pub default_include_globs: Vec,
- pub default_exclude_globs: Vec,
-}
-
-pub struct EmbeddingConfig {
- pub max_embed_bytes: u64,
- pub allow_resource_link: bool,
- pub redact_patterns: Vec, // regex patterns
-}
-```
-
-## Implementation Status
-
-**Current Phase**: SCAFF-01 (Scaffolding) - Complete
-
-All modules are stubs with `unimplemented!()`. Implementation will proceed in phases:
-
-### Phase 1: Path and Sandbox (Protocol tasks)
-- Path normalization (Windows, Linux, macOS)
-- Canonical path resolution (symlinks, junctions)
-- Containment checking
-- Blocklist matching
-
-### Phase 2: Tool Operations (Tool tasks)
-- File read with line ranges
-- File write with atomic operations
-- File edit with diff generation
-- Glob search
-- Grep search with context
-- LS directory listing
-- Terminal creation and lifecycle
-- Terminal output capture with ring buffer
-
-### Phase 3: Security and Integration (Integration tasks)
-- Permission prompt flow
-- Decision caching with TTL
-- Audit logging with structured fields
-- ACP tool call event generation
-- Tool title formatting
-
-## Integration Points
-
-### dirigent_core
-- Uses `dirigent_tools` for ACP client request handlers
-- Passes configuration from connector params
-- Routes tool operations through sandbox
-
-### api
-- May use `dirigent_tools` for server function implementations
-- Exposes configuration endpoints
-- Handles permission prompts via UI
-
-### web
-- Displays tool calls with titles, locations, diffs
-- Renders permission prompts
-- Shows audit logs
-
-## Testing Strategy
-
-Test infrastructure will be set up in SCAFF-03:
-- Unit tests for each module
-- Integration tests with mocker
-- Cross-platform tests (Windows, Linux, macOS)
-- Golden transcript fixtures
-- Test utilities for temp directories, file comparison, sandboxed environments
-
-## Related Packages
-- **dirigent_core** - Uses this package for tool operations
-- **api** - May use this package for server functions
-- **dirigent_protocol** - Shared event types for tool calls
-
-## References
-- Task file: `docs/building/04_acp_client/04_tasks_00_scaffolding_and_finishing.md`
-- Tools research: `docs/building/04_acp_client/03_tools_research.md`
-- Sandbox spec: `docs/building/04_acp_client/03_fs_sandboxing_and_permissions_spec.md`
-- Roadmap: `docs/building/04_acp_client/roadmap.md`
diff --git a/crates/opencode_client/CLAUDE.md b/crates/opencode_client/CLAUDE.md
deleted file mode 100644
index 46e859b..0000000
--- a/crates/opencode_client/CLAUDE.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Package: opencode_client
-
-Rust client library for interacting with the OpenCode.ai API.
-
-## Quick Facts
-- **Type**: Library
-- **Main Entry**: src/lib.rs
-- **Dependencies**: reqwest, serde, serde_json, chrono
-
-## Key Files
-- `src/lib.rs` - Public API exports
-- `src/types.rs` - OpenCode API type definitions (Session, Message, Part, etc.)
-- `src/client.rs` - HTTP client implementation with optional logging callbacks
-
-## Main Exports
-- `OpenCodeClient` - Main API client with methods: list_sessions, list_messages, send_message
-- `Session` - Session metadata and configuration
-- `Message` - User or Assistant message (tagged enum)
-- `MessageWithParts` - Message info + content parts
-- `Part` - Text, Reasoning, or workflow parts (StepStart, StepFinish, Tool)
-- `ClientError` - Error types: Http, Request, Serialization
-- `LogCallback` - Type alias for logging callbacks
-
-## Related
-- Used by: web, mobile (future), desktop (future)
-- Independent: Can be used in any Rust project
-
-## Documentation
-- README: ./README.md
-- API spec: ../../docs/api/opencode.md