# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Anteroom is a self-hosted, private ChatGPT-style web UI and agentic CLI that connects to any OpenAI-compatible API. It provides two interfaces: a FastAPI web UI with vanilla JS frontend, and a Rich-based CLI REPL with built-in tools and MCP integration. Single-user, local-first, SQLite-backed.

## Development Commands

```bash
pip install -e ".[dev]"             # Install for development
aroom                               # CLI REPL (default entry point)
aroom web                           # Web UI at http://127.0.0.1:8080
aroom chat --plan                   # Start in planning mode
aroom init                          # Interactive setup wizard
aroom --test                        # Validate AI connection
aroom --approval-mode auto          # Override safety mode
aroom --allowed-tools bash,write_file  # Pre-allow tools
aroom --denied-tools bash,run_agent   # Hard-block tools

pytest tests/ -v                    # All tests
pytest tests/unit/ -v               # Unit tests only
pytest tests/e2e/ -v                # E2e tests (requires uvx/npx)
pytest tests/e2e/ -m real_ai -v    # Agent evals (requires API key)
ruff check src/ tests/              # Lint
ruff format src/ tests/             # Format (120 char line length)
mypy src/ --ignore-missing-imports  # Type check (must pass with 0 errors)

# Evals and demos
npx promptfoo eval --config evals/promptfoo.yaml   # Prompt regression
npx promptfoo eval --config evals/agentic.yaml     # Agentic behavior
npx promptfoo redteam run --config evals/redteam.yaml  # Red teaming
cd demos && make demos              # Build demo GIFs (requires VHS)
```

### Worktree Development

When working with git worktrees (multiple features in parallel), each worktree **must** have its own virtual environment. Editable installs (`pip install -e .`) are global to the Python interpreter — the last worktree to install wins, causing import errors in all other worktrees.

```bash
# In each worktree:
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]" -q

# Run tests/lint with the worktree's venv:
.venv/bin/python -m pytest tests/unit/ -v
.venv/bin/ruff check src/ tests/
```

## Architecture

### Dual Interface, Shared Core

Both the web UI and CLI share the same agent loop (`services/agent_loop.py`) and storage layer. Changes to tool handling, streaming, or message building affect both interfaces.

```
Web UI (routers/)  ──┐
                     ├──→ agent_loop.py → ai_service.py → OpenAI-compatible API
CLI (cli/)         ──┘         │
  repl.py (main loop)    tools/ + mcp_manager.py
  commands.py (/cmds)         │
  renderer.py            storage.py → SQLite
```

### Key Modules

#### Entry Points & Core
- **`__main__.py`** — Argparse dispatch: `init`, `config`, `chat`, `exec`, `db`, `usage`, `audit`, `artifact`, `pack`, `space`, `mission`, `workflow`, `start`, `stop`, `status`, `unpack` subcommands. See source for flags and nested subparsers
- **`unpack.py`** — Extracts bundled auxiliary files (tests, docs, evals, etc.) to a directory for offline development. Used with `anteroom[kitchensink]` extra
- **`app.py`** — FastAPI app factory, middleware stack (auth, rate limiting, CSRF, security headers, body size limit). Auth token derived from Ed25519 identity key via HMAC-SHA256. Lifespan management: initializes encrypted database (if enabled), starts retention worker (if configured)
- **`config.py`** — YAML config loader: layered precedence (defaults < team < packs < personal < space < env < CLI). Dataclass hierarchy: `AppConfig` → `AIConfig`, `CliConfig`, `SafetyConfig`, `RagConfig`, `WorkflowConfig` (incl. `WorkflowCredentialConfig`), etc. Enforces team `enforce` list. Validated via `config_validator.py`
- **`identity.py`** — Ed25519 keypair generation, UUID4 user IDs, PEM serialization
- **`tls.py`** — Self-signed cert generation for localhost HTTPS

#### Services (Shared Core)
- **`services/agent_loop.py`** — Shared agentic loop: streams responses, parses tool calls, parallel execution via `asyncio.as_completed`, max 50 iterations. Cancel-aware. Auto-compacts at configurable token threshold. Supports prompt queuing, narration cadence, auto-plan threshold. Tool results bounded by `tool_output_max_chars` (live agent calls; conversation replay uses `tool_replay_max_chars` in the CLI resume path). Consecutive text-only response limit prevents runaway loops (configurable, default 3). Intra-response line repetition detection stops degenerate LLM output within a single streaming response (configurable, default 5 repeated lines). DLP scanning on streamed chunks and final assembled text (with `dlp_blocked` and `dlp_warning` events). Output content filtering with system prompt leak detection (with `output_filter_blocked` and `output_filter_warning` events). Internal `_`-prefixed metadata keys stripped before sending to LLM
- **`services/ai_service.py`** — OpenAI SDK wrapper with streaming, token refresh on 401, split timeout architecture (6 timeouts: connect/write/pool/first_token/request/chunk_stall), cancel-aware at all phases, exponential backoff retry on transient errors. Emits `phase`, `retrying`, `tool_call_args_delta`, `usage` events. Error events include `retryable` flag. `complete()` method for non-streaming completions used by context compaction. `create_ai_service()` factory routes by `ai.provider` to return the appropriate service implementation
- **`services/anthropic_provider.py`** — Native Anthropic API provider (`AnthropicService`). Activated when `ai.provider: anthropic`. Translates Anteroom's OpenAI-shaped message/tool schema to the Anthropic Messages API and back. Supports streaming, tool use, and the same event protocol as `ai_service.py`. Optional dependency: `pip install anteroom[anthropic]`
- **`services/litellm_provider.py`** — LiteLLM multi-provider wrapper (`LiteLLMService`). Activated when `ai.provider: litellm`. Supports 100+ LLM providers (OpenRouter, Replicate, Together, Cohere, etc.) via model name prefixes (e.g. `openrouter/openai/gpt-4o`). Uses `litellm.acompletion()` for streaming and tool calling. Matches the AIService interface and event protocol. Optional dependency: `pip install anteroom[providers]`
- **`services/storage.py`** — SQLite DAL with parameterized queries, UUID IDs. Vector storage via usearch. Source CRUD, tags, groups, space CRUD, text chunking, embeddings. Token usage tracking. Message metadata (JSON) for RAG provenance. Scoped semantic search, FTS5 keyword search
- **`services/mcp_manager.py`** — MCP client lifecycle: parallel startup, per-server tool filtering, routes `call_tool()` to correct session. Warns on tool-name collisions
- **`services/context_trust.py`** — Prompt injection defense: trust classification, defensive XML envelopes, sanitization
- **`services/tool_result_compact.py`** — Field-aware tool output compaction shared by live agent loop and conversation replay. Strips internal keys, preserves structured metadata, truncates bulk content with informative suffixes
- **`services/embeddings.py`** — Dual provider: `LocalEmbeddingService` (fastembed, offline-first, default) and `EmbeddingService` (OpenAI-compatible API)
- **`services/embedding_worker.py`** — Background worker for unembedded content. Exponential backoff, auto-disables after 10 failures, recovery probe every 10 minutes
- **`services/vector_index.py`** — Usearch-based vector indexes with string UUID key mapping, crash recovery, and iterative widening search. `VectorIndexManager` manages messages and source chunks indexes with startup rebuild from SQLite metadata
- **`services/document_extractor.py`** — Text extraction from uploaded files (PDF, DOCX, PPTX, XLSX). Returns `ExtractionResult` with text and warnings for graceful degradation
- **`services/rag.py`** — RAG pipeline with three retrieval modes (dense, keyword, hybrid). Dense: usearch vector search. Keyword: FTS5 full-text search. Hybrid: reciprocal rank fusion of both. Optional cross-encoder reranking. `retrieve_context()` returns `(chunks, reason)` tuple — reason is `None` on success or a human-readable string on degradation. Space-scoped retrieval, token budget trimming, graceful degradation
- **`services/reranker.py`** — Cross-encoder reranker for RAG result re-scoring. Local (fastembed TextCrossEncoder, offline) or API provider. Configurable top-K, score threshold, candidate multiplier. Graceful degradation
- **`services/codebase_index.py`** — Tree-sitter codebase index for token-efficient context injection. 10 languages. Graceful degradation. Optional: `pip install anteroom[index]`
- **`services/audit.py`** — Structured JSONL audit log with HMAC-SHA256 chain tamper protection. Append-only, daily/size rotation, retention purge. SIEM-compatible
- **`services/dlp.py`** — Data Loss Prevention scanner: regex-based sensitive pattern detection (SSN, credit card, etc.). Actions: redact/block/warn. Custom patterns via config
- **`services/injection_detector.py`** — Prompt injection detection: canary tokens, encoding attacks, heuristic patterns. Actions: block/warn/log
- **`services/output_filter.py`** — Output content filter: system prompt leak detection (n-gram analysis). Actions: redact/block/warn
- **`services/egress_allowlist.py`** — Egress domain allowlist for outbound API requests. Fails closed on unparseable URLs
- **`services/config_editor.py`** — Shared config read/write service with scoped persistence (personal, space, project), team enforcement, field validation, and 7-layer source attribution
- **`services/config_overlays.py`** — Pack config overlay collection, merging, and conflict detection. Priority-based merge (lower wins), dot-path collision detection, config source tracking for `--with-sources`, enforced field validation
- **`services/pack_sources.py`** — Git-based pack source cache. Clone/pull repos, URL scheme allowlist, credential sanitization, deterministic cache paths via SHA-256
- **`services/packs.py`** — Pack management: YAML manifests, install/remove with reference counting. `resolve_pack()` with collision detection. Path traversal prevention. Enumerated SQL columns
- **`services/pack_refresh.py`** — Background worker: auto-clones and pulls pack source repos, installs/updates on content changes. Exponential backoff
- **`services/pack_lock.py`** — Lock file for reproducible installs. Content hashes, source URLs. `validate_lock()` compares lock vs DB
- **`services/event_bus.py`** — Async pub/sub: in-process via `asyncio.Queue`, cross-process via SQLite `change_log` polling
- **`services/slug.py`** — Slug generation: unique `{word}-{word}` names for conversation resumption
- **`services/trust.py`** — Trust store for ANTEROOM.md files. SHA-256 hash verification. Fails closed
- **`services/team_config.py`** — Team config discovery, loading, merging (deep_merge with named-list support), enforcement
- **`services/config_validator.py`** — Schema validation for raw YAML config dicts. Collects all errors/warnings
- **`services/compliance.py`** — Compliance rules engine: declarative config policy validation. `validate_compliance()` evaluates `ComplianceRule` entries (must_be, must_not_be, must_match, must_not_be_empty, must_contain) against the final merged `AppConfig`. Fails closed at startup. `aroom config validate` CLI subcommand. Redacts sensitive fields in violation output
- **`services/config_watcher.py`** — Mtime-based config file watcher for live reload
- **`services/discovery.py`** — Walk-up directory discovery. Searches `.anteroom/`, `.claude/`, `.parlor/` with precedence
- **`services/required_keys.py`** — Required keys validation and interactive prompting
- **`services/tool_rate_limit.py`** — Tool call rate limiting: per-minute, per-conversation, and consecutive-failure caps. Block or warn action. Shared across parent and sub-agents
- **`services/session_store.py`** — Memory and SQLite session backends with idle/absolute timeouts
- **`services/ip_allowlist.py`** — IP allowlist checking with CIDR and exact address support. Fails closed on invalid input
- **`services/retention.py`** — Background data retention worker. Purges conversations older than configured retention days with cascade deletes. Exponential backoff on failures
- **`services/workflow_engine.py`** — Workflow engine: step execution (runner/gate/loop/parallel/llm), locks, heartbeat, resume, cancel, compensation, repair, loop break detection
- **`services/workflow_credentials.py`** — Named credential resolution: env vars or shell commands. Values never logged or persisted
- **`services/workflow_storage.py`** — CRUD for workflow runs, steps, events, approvals, locks, schedules, caching. Atomic lock, cancel/approval helpers, repair
- **`services/message_router.py`** — Message-to-run router: keyword heuristics classify follow-up messages against active workflow runs (attach, update, cancel, replace, spawn, ask_user)
- **`services/workflow_runners.py`** — Runner registry and execution. Agent and opaque runners with artifact injection. Workflow input drainer polls `workflow_run_inputs` and injects pending messages via message_queue
- **`services/workflow_hooks.py`** — Notification hooks: webhook and Unix socket with bounded drain
- **`services/workflow_publishers.py`** — Publish adapters for workflow output steps: file and webhook. Adapter registry
- **`services/workflow_executor.py`** — Background run dispatcher with atomic claim and crash recovery
- **`services/workflow_resolution.py`** — Shared workflow definition path resolution
- **`services/workflow_scheduler.py`** — Schedule evaluation, due-schedule claiming, run enqueue
- **`services/workflow_simulator.py`** — Workflow simulation engine for testing definitions with stub runners
- **`services/cron.py`** — 5-field cron evaluator with safety bounds
- **`services/encryption.py`** — SQLCipher encryption at rest (optional). Key derived from Ed25519 via HKDF-SHA256. Graceful degradation to standard sqlite3
- **`services/artifacts.py`** — Universal artifact model with FQN parsing, 7 types, 6 source layers, SHA-256 deduplication
- **`services/artifact_storage.py`** — Artifact CRUD with version tracking and space-scoped filtering
- **`services/artifact_registry.py`** — In-memory artifact index with 6-layer precedence
- **`services/artifact_health.py`** — Health checks: conflicts, collisions, shadows, orphans. `--fix` auto-resolves
- **`services/starter_packs.py`** — Built-in pack templates. Starter packs auto-install on first run, example packs require manual install
- **`services/rule_enforcer.py`** — Hard rule enforcement at tool execution. Checks `enforce: hard` rules before every tool call, returns `(blocked, reason, rule_fqn)`
- **`services/pack_attachments.py`** — Pack attachment tracking at global/space scopes with priority-based conflict resolution
- **`services/local_artifacts.py`** — Discovers artifacts from `.anteroom/artifacts/`, `.anteroom/skills/`, `.claude/skills/`, `.claude/rules/`. Path traversal prevention
- **`services/artifact_import.py`** — Bulk import into artifact DB. Format detection (YAML/Markdown), collision detection, version init
- **`services/spaces.py`** — Space file parser: `SpaceConfig`/`SpaceLocalConfig` frozen dataclasses. Name validation, URL scheme validation, 256KB size limit. YAML serialization. Local vs global space detection
- **`services/space_storage.py`** — Space DB CRUD with column-allowlisted updates. `resolve_space()` with collision detection. `resolve_space_by_cwd()` with parent walk-up. Cascade deletes
- **`services/space_bootstrap.py`** — First-load cloning (shallow `--depth=1`) and pack installation. URL scheme validation, credential sanitization, 120s timeout
- **`services/space_watcher.py`** — Mtime-based space file watcher for hot-reload. TOCTOU-safe
- **`services/server_manager.py`** — Background web server process management with PID file lifecycle, cross-platform start/stop, TCP port probing, log rotation
- **`services/spec_schema.py`** — Spec schema: dataclasses, YAML parsing, phase metadata, templates. Topological sort for task DAGs
- **`services/spec_generator.py`** — LLM-assisted spec generation from prompts and GitHub issues
- **`services/spec_dashboard.py`** — Portfolio dashboard: aggregates lifecycle state, phase statuses, run counts. Filters by status, namespace, phase, has_runs
- **`services/spec_service.py`** — Spec lifecycle: create, read, update, approve/unapprove, traceability, task launch, approval guard
- **`services/spec_diff.py`** — Spec diffing and deterministic invalidation: three rules, stale reason derivation
- **`services/spec_queue.py`** — Review inbox: aggregates specs by phase status, attachment-aware filtering
- **`services/spec_linkage.py`** — Git branch/PR linkage for specs: metadata storage, CLI auto-detection, deduplication
- **`services/spec_conformance.py`** — Deterministic conformance checks: task coverage, run health, phase status, temporal validity
- **`services/spec_gates.py`** — Workflow gate conditions for spec phases
- **`services/workflow_cache.py`** — LLM response caching for workflow steps. Content hashing, configurable TTL, invalidation
- **`services/workflow_diagnosis.py`** — Diagnosis engine for non-success workflow runs. Pattern-matches stop reasons, extracts evidence
- **`services/async_tasks.py`** — Helpers for safely cancelling asyncio tasks without leaking late exceptions
- **`services/mission_storage.py`** — Mission CRUD for sessions, items, dependencies, executions, revisions, and events. Scheduler eligibility queries
- **`services/mission_compiler.py`** — Spec-backed (deterministic) and prompt-backed (LLM) plan compilation. Structured patch operations for plan mutation. Execution profile integration
- **`services/mission_profiles.py`** — Execution profiles: preference-driven binding selection with workflow discovery, scoring, and fallback. Built-in profile registry
- **`services/mission_scheduler.py`** — Deterministic eligibility evaluation, lane/concurrency limits, adapter dispatch, heartbeat polling
- **`services/mission_adapters.py`** — Adapter protocol and implementations: NoopAdapter (instant completion), WorkflowAdapter (enqueues workflow runs)
- **`services/external_checks.py`** — GitHub external reality checks via `gh` CLI: issue-closed and merged-PR detection for mission auto-reconciliation. Fail closed on errors

#### Web UI (routers/)
- **`routers/chat.py`** — SSE chat streaming with dataclass-based architecture. Per-request registries for space-scoped artifacts, skills, and rule enforcement. Supports prompt queuing, source injection, plan mode, sub-agents, DLP scanning, output filtering
- **`routers/sources.py`** — Sources API: CRUD, file upload, tags, groups, space linking, `POST /sources/{id}/reprocess` for re-extraction and re-chunking
- **`routers/search.py`** — Semantic (vector) and hybrid (FTS5 + vector) search. Requires usearch
- **`routers/proxy.py`** — OpenAI-compatible proxy for external tools. Opt-in via `proxy.enabled`
- **`routers/approvals.py`** — Web UI safety gate approval flow. Atomic dict pop prevents TOCTOU races
- **`routers/events.py`** — SSE endpoint for real-time UI updates (canvas streaming, approvals)
- **`routers/usage.py`** — Token usage statistics endpoint with per-model aggregation and cost estimates
- **`routers/plan.py`** — Plan mode endpoints: read, approve, reject
- **`routers/artifacts.py`** — Artifact API: `GET /api/artifacts` (list with type/namespace/source filters), `GET /api/artifacts/{fqn}` (show with version history), `DELETE /api/artifacts/{fqn}` (delete artifact and refresh registry)
- **`routers/artifact_health.py`** — Artifact health check endpoint: `GET /api/artifacts/check` (triggers health check, no `fix` parameter). Returns `HealthReport` JSON
- **`routers/packs.py`** — Pack API (read-only Phase 2): `GET /api/packs` (list with artifact counts), `GET /api/packs/{namespace}/{name}` (show with artifact details), `GET /api/packs/by-id/{pack_id}` (show by pack ID), `DELETE /api/packs/by-id/{pack_id}` (remove by pack ID). Strips `source_path` from responses to prevent info disclosure
- **`routers/spaces.py`** — Spaces API: `GET/POST /api/spaces` (list/create), `GET/DELETE /api/spaces/{id}` (show/delete), `GET /api/spaces/{id}/paths` (mapped dirs), `POST /api/spaces/{id}/refresh` (re-parse YAML), `GET/POST/DELETE /api/spaces/{id}/sources` (source linking; `?link_type=direct` returns only directly-linked sources), `GET /api/spaces/{id}/packs` (attached packs). Each space response includes `origin` field indicating "local" (space-scoped, in `.anteroom/`) or "global" (user-scoped, in `~/.anteroom/spaces/`). Pydantic validation on name pattern and path traversal. Error messages sanitized to prevent path disclosure
- **`routers/config_api.py`** — Config editing API: `GET /api/config/schema` (config field schema for TUI), `GET /api/config` (current merged config), `PATCH /api/config` (atomic updates with layer-aware merging), `GET /api/config/layers` (layer composition), `POST /api/config/validate` (dry-run validation)
- **`routers/specs.py`** — Spec lifecycle API: list, create, show, approve/unapprove, diff, stale, portfolio dashboard. FQN/phase validation
- **`routers/workflows.py`** — Workflow monitoring and repair API: list definitions, list/filter runs, run detail with steps, event history. Action endpoints: cancel, approve, reject. `PATCH /workflow-runs/{run_id}/repair` for governed field edits (#995)

#### CLI Modules
- **`cli/repl.py`** — Main REPL loop with prompt_toolkit. Orchestrates: system prompt building, space context, trust verification, plan mode, skill auto-invocation. Delegates slash commands to `commands.py`. Manages `/pack`, `/space`, `/artifact`, `/config`, `/spec`, `/reprocess` commands. `/instructions` alias for `/conventions`. Space auto-detection from cwd with parent walk-up. Skill args sanitized with `sanitize_trust_tags()` and delimited with `<skill_args>` tags for injection defense
- **`cli/themes.py`** — CLI color theme system. `CliTheme` frozen dataclass with semantic color slots. Four built-in themes (midnight, dawn, high-contrast, accessible). NO_COLOR support
- **`cli/layout.py`** — Shared CLI layout utilities: prompt prefix styling with approval-mode-aware colors, input lexer for `/command` highlighting, path shortening. Exports: `InputLexer`, `input_line_prefix`, `set_approval_mode`, `_shorten_path`
- **`cli/commands.py`** — Shared slash-command engine. `CommandKind` Literal type classifies 35+ commands, `CommandResult` frozen dataclass describes outcomes, `CommandContext` carries state. Pure dispatch via `execute_slash_command()`. Centralizes `ALL_COMMAND_NAMES`, `COMMAND_DESCRIPTIONS`, `SUBCOMMAND_COMPLETIONS` as single source of truth. `get_builtin_names()` prevents skill name collisions
- **`cli/renderer.py`** — Rich terminal output: thinking spinner, plan checklist, inline diffs, tool call dedup, subagent rendering, RAG status feedback. `use_stdout_console()` for REPL-compatible mode via raw stderr fd. `write_raw()` for direct terminal fd writes bypassing patch_stdout (used by approval/ask_user sub-prompts)
- **`cli/exec_mode.py`** — Non-interactive mode for scripting/CI. JSON output, timeout, fail-closed approval. Exit codes: 0/1/124
- **`cli/plan.py`** — Planning mode helpers: `PLAN_MODE_ALLOWED_TOOLS`, plan file I/O, plan command parsing, `enter_plan_mode()`, `leave_plan_mode()`
- **`cli/instructions.py`** — ANTEROOM.md discovery (`.anteroom.md` > `ANTEROOM.md`, walk-up from cwd), global instructions, token estimation
- **`cli/skills.py`** — Skills registry with namespace-aware resolution. Loads from built-in, global, and space-scoped directories. Auto-invocation via synthetic `invoke_skill` tool. Artifact bridge for pack-sourced skills
- **`cli/config_tui.py`** — Full-screen interactive config editor TUI (prompt_toolkit Application, same pattern as resume picker). Two-panel layout, arrow-key navigation, scope cycling, inline editing, save/discard. Used by `/config` command
- **`cli/workflow_cli.py`** — CLI handlers for `aroom workflow` subcommands (run/status/list/history/resume/repair/cancel). `--force` drift override, budget display, live transcript rendering with `--no-transcript` suppression
- **`cli/mission_cli.py`** — CLI handlers for `aroom mission` subcommands (create/list/status/talk/update/revisions/cancel). Spec and prompt compilation, patch preview, conversational REPL launch
- **`cli/workflow_fmt.py`** — Shared timeline formatters for workflow CLI: status icons, friendly durations, step line rendering, run headers
- **`cli/transcript_renderer.py`** — Renders workflow transcript events in Rich for CLI display. Delegates to chat renderer helpers for visual parity

#### Tools
- **`tools/`** — ToolRegistry: `_handlers` + `_definitions`. Built-in: read_file, write_file, edit_file, bash, glob_files, grep, create_canvas, update_canvas, patch_canvas, run_agent, ask_user, introspect. Optional (with `anteroom[office]`): docx, xlsx, pptx. Safety gate: tier check → pattern detection → hard-block. File-modifying tools return `_old_content`/`_new_content` for diff rendering (stripped before LLM). `check_safety()` and `call_tool()` accept optional `rule_enforcer_override` kwarg for per-request space-scoped rule enforcement without mutating shared state
- **`tools/tiers.py`** — Risk tiers: READ/WRITE/EXECUTE/DESTRUCTIVE. Approval modes: AUTO/ASK_FOR_DANGEROUS/ASK_FOR_WRITES/ASK. Unknown/MCP tools default to EXECUTE
- **`tools/bash.py`** — Shell command execution with configurable sandboxing. `_check_sandbox()` enforces network/package/path/command restrictions before execution. Accepts `_sandbox_config: BashSandboxConfig` from `call_tool()`. Accepts optional `_env: dict` for credential binding (#970) merged into subprocess environment. On Windows: assigns subprocess to Win32 Job Object for kernel-level resource limits; rewrites multiline `python -c` commands to temp `.py` files to avoid `cmd.exe` truncation; resolves `python3` → `python` when `python3` is unavailable. Configurable timeout caps, output truncation, and audit logging via `security_logger`
- **`tools/security.py`** — Security utilities: hard-block patterns, path validation, `check_network_command()`, `check_package_install()`, `check_blocked_path()`, `check_custom_patterns()` for sandbox enforcement. Cross-platform: Unix tools, PowerShell, Windows package managers
- **`tools/sandbox_win32.py`** — Win32 Job Object sandbox via ctypes (no dependencies). `create_job_object()`, `assign_process()`, `terminate_job()`, `close_job()`, `setup_job_for_process()`. Enforces memory, process count, and CPU time limits. No-op on non-Windows. All functions return success/failure, never raise
- **`tools/safety.py`** — Pure detection: `check_bash_command()` (regex patterns), `check_write_path()` (sensitive paths). Returns `SafetyVerdict` with `is_hard_blocked`
- **`tools/canvas.py`** — Canvas create/update/patch with SSE streaming support. Excalidraw diagram language generates JSON scene data rendered in web UI via iframe viewer
- **`tools/mission.py`** — 10 mission steering tools: list/status/hold/resume/drop/reprioritize/add/change deps/change binding/replan. Registered via `register_mission_tools()`
- **`tools/subagent.py`** — `run_agent` tool: isolated child AI sessions, same safety gates. Guarded by `SubagentLimiter`. Configurable via `safety.subagent`
- **`tools/introspect.py`** — Lets AI examine its own runtime context. READ tier (auto-allowed)
- **`tools/office_com.py`** — Shared COM lifecycle manager for Windows. Optional: requires pywin32
- **`tools/office_docx.py`** — DOCX tool via python-docx or COM. WRITE tier. Actions: create, read, edit, styles, export_pdf (COM), find_regex, etc.
- **`tools/office_xlsx.py`** — XLSX tool via openpyxl or COM. WRITE tier. Actions: create, read, edit, format, charts, pivot_tables (COM), etc.
- **`tools/office_pptx.py`** — PPTX tool via python-pptx or COM. WRITE tier. Actions: create, read, edit, insert_image/shape, master_layout, etc.

### Security Model

Single-user local app, OWASP ASVS Level 2. Auth: HttpOnly session cookies + CSRF double-submit + Origin validation. Stable auth token from Ed25519 key via HMAC-SHA256. Session store (memory or SQLite-backed) tracks creation time, last activity, and client IP for session validation and lifecycle management. IP allowlisting (CIDR or exact) gates access at middleware. Concurrent session limits prevent token reuse abuse. Session timeouts: 12-hour absolute, 30-minute idle. Middleware: rate limiting (120 req/min), body size (15MB), security headers. Tool safety: 4 risk tiers, 4 approval modes, 3 permission scopes (once/session/always). Path traversal and hard-block detection. Bash sandboxing: configurable network/package/path/command restrictions, timeout caps, output limits, audit logging. MCP tools gated at parent and sub-agent levels. Fails closed: no approval channel = blocked. Encryption at rest: optional SQLCipher integration (opt-in via `encrypt_at_rest`), key derived from Ed25519 identity key via HKDF-SHA256. Data retention: configurable policy with background worker, purges conversations and attachments older than retention days; cascades delete related messages, tool calls, embeddings.

### Database

SQLite with WAL journaling, FTS5 for search, foreign keys enforced. Optional SQLCipher encryption at rest (via `encrypt_at_rest` config). Schema in `db.py`. Key tables: conversations, messages, sources, tool_calls, canvases, artifacts, packs, spaces, workflows, workflow_llm_cache, mission_sessions/mission_items/mission_item_dependencies/mission_executions/mission_revisions/mission_events. Vector similarity via usearch. Retention cascades deletes.

### Configuration

Config at `~/.anteroom/config.yaml` (backward compat: `~/.parlor/config.yaml`). Env vars override with `AI_CHAT_` prefix. Dynamic API key refresh via `api_key_command`. Ed25519 identity auto-generated on first run.

**Precedence:** defaults < team < **packs** < personal < space < env vars < CLI flags (team-enforced fields override all). Pack config overlays are collected from the DB at startup and merged by priority (lower number wins) into the config chain. Space configs require SHA-256 trust verification. Live reload via config watcher.

Key config sections (see `config.py` dataclasses for all fields and defaults):
- **`AIConfig`** — API connection, 6 timeouts, retry settings, narration cadence, max_tools (default 128), temperature (None = provider default), top_p (None = provider default), seed (None = provider default), egress domain allowlist (`allowed_domains`, `block_localhost_api`). `provider` (default `"openai"`) selects the AI backend: `openai` for any OpenAI-compatible API, `anthropic` for native Anthropic Messages API, `litellm` for 100+ LLM providers via LiteLLM (OpenRouter, Replicate, Together, etc.). `max_output_tokens` (default 4096) caps generated tokens per response for providers that require this parameter. `model_family_aliases` (default `{}`) maps short family names (e.g. `haiku`, `sonnet`) to full model IDs for sub-agent routing
- **`RateLimitConfig`** — HTTP request rate limiting (sliding window): `max_requests` (default 120), `window_seconds` (default 60), `exempt_paths` (default `/api/events` for SSE), `sse_retry_ms` (default 5000 for EventSource retry backoff). Defends against EventSource reconnection floods and request storms. Returns 429 Too Many Requests when exceeded
- **`SafetyConfig`** — Approval mode (default ask_for_writes), allowed/denied tools, custom bash patterns, per-tool tier overrides, read-only mode, tool rate limiting (per-minute, per-conversation, consecutive failures). Nested `BashSandboxConfig`: execution timeout (1-600s), output limits (min 1000 chars), path/command blocking, network/package restrictions, audit logging. Nested `OsSandboxConfig`: Win32 Job Object limits — `max_memory_mb` (512), `max_processes` (10), `cpu_time_limit` (None). Auto-detects Windows
- **`CliConfig`** — Context compaction thresholds, tool dedup, retry behavior, visual thresholds, theme selection (`theme`, default "midnight"), tool output limits (`tool_output_max_chars` for live calls, `tool_replay_max_chars` for conversation resume, both default 2000), consecutive text-only loop limit (`max_consecutive_text_only`, default 3, 0 to disable), intra-response line repetition limit (`max_line_repeats`, default 5, 0 to disable)
- **`PlanningConfig`** — Auto-trigger: `auto_mode` (off/suggest/auto), `auto_threshold_tools`
- **`SkillsConfig`** — `auto_invoke` (default true) enables AI skill invocation
- **`SubagentConfig`** — Limits: concurrency (5), total (10), depth (3), iterations (15), timeout (120s)
- **`EmbeddingsConfig`** — Dual provider (local fastembed default or API). Tri-state `enabled`: None=auto-detect, True=force, False=disable. `cache_dir` for vendored/offline model loading
- **`RagConfig`** — RAG pipeline: `max_chunks` (10), `max_tokens` (2000), `similarity_threshold` (0.5), `retrieval_mode` (dense/keyword/hybrid, default dense)
- **`RerankerConfig`** — Cross-encoder reranker: `enabled` (None=auto-detect), `provider` (local only), `model`, `top_k` (5), `score_threshold` (0.0), `candidate_multiplier` (3), `cache_dir` for vendored/offline model loading
- **`CodebaseIndexConfig`** — Tree-sitter index: `map_tokens` (1000), auto-detect languages. Optional dependency
- **`ProxyConfig`** — OpenAI-compatible proxy (opt-in), CORS allowlist
- **`McpServerConfig`** — Per-server `tools_include`/`tools_exclude` (fnmatch), `trust_level` (default `"untrusted"`; controls defensive prompt envelope wrapping for tool outputs)
- **`SessionConfig`** — Session management: `store` (memory/sqlite), `max_concurrent_sessions` (0 = unlimited), `idle_timeout` (1800s), `absolute_timeout` (43200s), `allowed_ips` (CIDR or exact; empty = allow all), `log_session_events` (bool)
- **`StorageConfig`** — Data retention and encryption: `retention_days` (0 = disabled), `retention_check_interval` (default 3600s), `purge_attachments` (default true), `purge_embeddings` (default true), `encrypt_at_rest` (default false, requires sqlcipher3), `encryption_kdf` (default hkdf-sha256)
- **`AuditConfig`** — Structured audit log: `enabled` (default false), `log_path`, `tamper_protection` (hmac/none), `rotation` (daily/size), `retention_days` (90), `redact_content` (true), per-event-type toggles
- **`OutputFilterConfig`** — Output content filtering: `enabled` (default false), `system_prompt_leak_detection` (default true), `leak_threshold` (0.0-1.0, default 0.4), `custom_patterns` (regex list for forbidden patterns), `action` (redact/block/warn, default warn), `redaction_string` (default `[FILTERED]`), `log_detections` (default true). Nested `OutputFilterPatternConfig`: `name`, `pattern` (regex), `description`
- **`ComplianceConfig`** — Declarative compliance rules: `rules` (list of `ComplianceRule`). Each rule: `field` (dot-path), `must_be`, `must_not_be`, `must_match` (regex), `must_not_be_empty`, `must_contain`, `message`. Evaluated at startup; non-compliant configs fail closed
- **`PackSourceConfig`** — Git-based pack source repos: `url`, `branch` (main), `refresh_interval` (30 min), `auto_attach` (bool, true), `priority` (1-100, default 50). URL scheme validation (rejects ext::, file://)

### Developer Workflow

Claude Code skills (`.claude/commands/`) and auto-loaded rules (`.claude/rules/`) enforce development standards. See `VISION.md` for product identity and scope guardrails.

**Skills**: `/ideate`, `/new-issue`, `/start-work`, `/commit`, `/submit-pr`, `/pr-check`, `/code-review`, `/deploy`, `/write-docs`, `/dev-help`, `/calm-dev-loop`, `/next`, `/triage`, `/cleanup`, `/a-help`

**Rules**: commit format, issue requirement, output formatting, vision alignment, security patterns, test requirements, feature parity

### Deployment

PyPI: `anteroom`. Deploy via `/deploy` skill (merge PR, CI, version bump, build, `twine upload`).

**Optional Dependencies** (declared in `pyproject.toml`):
- **`anthropic`** — `anthropic>=0.40.0`. Required when `ai.provider: anthropic`. Enable with: `pip install anteroom[anthropic]`
- **`providers`** — `litellm>=1.55.0`. Required when `ai.provider: litellm`. Enables access to 100+ LLM providers (OpenRouter, Replicate, Together, Cohere, etc.). Enable with: `pip install anteroom[providers]`
- **`office`** — `python-docx>=1.0`, `openpyxl>=3.1.0`, `python-pptx>=1.0`. Required for built-in docx/xlsx/pptx tools. Enable with: `pip install anteroom[office]`
- **`office-com`** — `python-docx>=1.0`, `openpyxl>=3.1.0`, `python-pptx>=1.0`, `pywin32>=306` (Windows only). Full COM backend for native Office automation. Enable with: `pip install anteroom[office-com]`
- **`encryption`** — `sqlcipher3>=0.5.0`. Required only if `config.storage.encrypt_at_rest: true`. Enable with: `pip install anteroom[encryption]`
- **`kitchensink`** — Dev tools (pytest, ruff, mypy, etc.) for offline development. Bundles tests, docs, evals inside the wheel. Extract with `aroom unpack <dest>`. Enable with: `pip install anteroom[kitchensink]`

## Terminology

- **Source** — a knowledge source (document, URL, text) used for RAG context injection. Managed via `services/storage.py`, API at `routers/sources.py`
- **Artifact source** (`ArtifactSource` enum) — the origin layer of an artifact: `built_in`, `global`, `team`, `project`, `local`, `inline`. Determines precedence in the artifact registry
- **Convention** / **Instruction** — space-level guidance loaded from `ANTEROOM.md`. The `/conventions` and `/instructions` REPL commands are aliases for the same feature
- **Skill** — a YAML-defined prompt template invoked via `/skill_name` or the `invoke_skill` tool. Loaded from filesystem directories and the artifact registry
- **Pack** — a named bundle of artifacts installed from a YAML manifest. Managed via `services/packs.py`. Pack attachments use `project_path` internally (DB column) but user-facing terminology is "directory-scoped"
- **Space** — a workspace binding that auto-detects workspace context from the working directory, bundles repositories, tools, configs, and packs into named workspaces. Unified workspace primitive replacing Projects. Managed via `services/spaces.py` and `services/space_storage.py`

## Testing Patterns

- **Unit tests** (`tests/unit/`, ~6,000 tests): fully mocked, no I/O. `@pytest.mark.asyncio` with `asyncio_mode = "auto"`
- **Integration** (`tests/integration/`): real SQLite databases
- **E2e** (`tests/e2e/`): real servers, mock AI. Markers: `e2e`, `requires_mcp`
- **Agent evals** (`tests/e2e/test_agent_evals.py`): 10 tests with real AI via `aroom exec --json`. Marker: `real_ai`. Auto-skip without API key. Uses `--temperature 0 --seed 42` for reproducibility
- **Prompt regression** (`evals/`): promptfoo suites via OpenAI-compatible proxy. `promptfoo.yaml` (11 prompt regression tests), `agentic.yaml` (6 exec-mode tests), `redteam.yaml` (adversarial). Run: `npx promptfoo eval --config evals/promptfoo.yaml`
- **Demo recordings** (`demos/`): VHS tape scripts producing reproducible GIFs. 3 demos: quickstart, tools, exec-mode. Run: `cd demos && make demos`
- **Type checking**: `mypy src/ --ignore-missing-imports` must pass with zero errors. All new and modified Python code must be fully type-annotated. Do not introduce new mypy errors — fix any that exist in files you touch
- Coverage target: 80%+. See `docs/advanced/testing.md` for full guide

## CI

GitHub Actions: Python 3.10-3.14 matrix, ruff lint+format, mypy type check, pytest with coverage, pip-audit, Semgrep SAST, CodeQL.
