ai-counsel
CLAUDE.md1 starsChanged 12 months agoMITView on GitHub
- Installs packages
We split this CLAUDE.md into the rules it gives an agent
15 rules. About 0 are common, 9 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈9
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 45
Working Directory Isolation: All adapters run subprocesses from working_directory parameter (client's current directory), preventing models …
- Distinctive: line 49
Not recommended: <3B parameters (struggle with vote formatting, echo prompts)
- Very specific: line 64
All CLI adapters run subprocesses from the working_directory passed by MCP clients
- Distinctive: line 66
Claude: Default isolation via subprocess cwd - models only see files in working directory
- Distinctive: line 131
Check early stopping: if ≥66% want to stop → break
- Distinctive: line 132
After round 2+: convergence detection compares current vs previous round
- Very specific: line 133
If converged/impasse/early-stop: stop early.
- Distinctive: line 248
Default: 60s per invocation
- Distinctive: line 257
Use cases: cost control, testing, staged rollout, performance tuning, compliance restrictions
- Very specific: line 264
Important: Must be <= rounds - 1 for convergence info.
- Very specific: line 293
Empty string: No default, no per-participant → {reasoning_effort} becomes ""
- Distinctive: line 492
Write test first (TDD)
- Very specific: line 494
Run unit tests: pytest tests/unit -v
- Distinctive: line 497
Update CLAUDE.md if architecture changes
- Distinctive: line 498
Commit with clear message
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
- Last updated: 2026-02-15
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
- AI Counsel: MCP server; multi-model debate; shared prior responses; iterative consensus.
- Key differentiation: Models engage in actual debate with cross-pollination (not just parallel aggregation).
- Production Ready: 113+ passing tests, type-safe validation, graceful error handling, structured logging, convergence detection, AI-powered summaries, evidence-based deliberation with secure tool execution.
Architecture
Core Components
- MCP Server Layer (
server.py) - Entry point for MCP protocol communication via stdio
- Exposes 2 MCP tools:
deliberateandquery_decisions(when decision graph enabled) - Handles JSON serialization and response truncation for token limits
- Logs to
mcp_server.log(not stdout/stderr to avoid stdio interference) - Deliberation Engine (
deliberation/engine.py) - Orchestrates multi-round debates between models
- Manages context building from previous responses
- Coordinates convergence detection and early stopping
- Initializes AI summarizer with fallback chain: Claude Sonnet → gpt-6.1-sol → Droid → Gemini
- Integrates tool execution system for evidence-based deliberation
- Tool Execution System (
deliberation/tools.py,models/tool_schema.py) - Base:
BaseToolwithexecute()method and security controls - Tools:
ReadFileTool(configurable size limit),SearchCodeTool,ListFilesTool,RunCommandTool(whitelist: ls, grep, find, cat, head, tail),GetFileTreeTool(ASCII output for clean JSON) - Orchestrator:
ToolExecutorparses TOOL_REQUEST markers, validates, routes to tools - Security: Whitelisted commands, file size limits, timeout protection (10s default), path exclusion patterns (prevents context contamination)
- Path Exclusions: Configurable patterns to block access to sensitive directories (e.g.,
transcripts/,.git/,node_modules/) - File Tree Rendering: Uses ASCII characters (
|--) in tool responses for clean JSON serialization; Unicode box-drawing (├──) in Round 1 prompts for better readability - Context injection: Tool results visible to all participants in subsequent rounds
- CLI Adapters (
adapters/base.py,adapters/claude.py, etc.) - Base:
BaseCLIAdapterhandles subprocess execution, timeout, error handling - Concrete:
ClaudeAdapter,CodexAdapter,DroidAdapter,GeminiAdapter,LlamaCppAdapter - Factory pattern in
adapters/__init__.pycreates adapters from config - Working Directory Isolation: All adapters run subprocesses from
working_directoryparameter (client's current directory), preventing models from analyzing the wrong repository - LlamaCpp Auto-Discovery: Resolves model names to file paths automatically, searches default paths, supports fuzzy matching
- Model Size Recommendations:
- Minimum: 7B-8B parameters (Llama-3-8B, Mistral-7B, Qwen-2.5-7B)
- Not recommended: <3B parameters (struggle with vote formatting, echo prompts)
- Token limits: Use 2048+ tokens for complete responses
- Reasoning Effort (config-based defaults with per-participant override):
- Controls reasoning depth per model via
{reasoning_effort}placeholder in CLI args - Codex:
low,medium,high,xhigh,max,ultra(Ultra requires GPT-6 Astra/Sol) (via-c model_reasoning_effort="{reasoning_effort}") - Droid:
off,low,medium,high(via-r {reasoning_effort}flag) - Claude:
low,medium,high,xhigh,max(current Fable 5.1 and Opus/Sonnet/Haiku 5.5) - Claude validation:
_supports_effort()checks current aliases and supported full IDs - Other adapters: N/A (reasoning_effort ignored)
- Priority chain: Per-participant → Config
default_reasoning_effort→ Empty string - Placeholder mechanism:
{reasoning_effort}in args is substituted at runtime - Config default: Set
default_reasoning_effortfield on adapter inconfig.yaml - Runtime override: Pass
reasoning_effortonParticipantobject
- Controls reasoning depth per model via
- Working Directory Isolation (
adapters/base.py) - All CLI adapters run subprocesses from the
working_directorypassed by MCP clients - Prevents models from analyzing the AI Counsel codebase when deliberating about user projects
- Claude: Default isolation via subprocess cwd - models only see files in working directory
- Gemini: Workspace boundaries via
--include-directoriesflag + subprocess cwd - strict isolation - Codex: Known limitation - can access any file regardless of working_directory (no true isolation)
- Droid: Subprocess cwd provides basic isolation (no additional flags needed)
- Flow: MCP client's cwd →
deliberatetool'sworking_directory→ adapter subprocess cwd → model sees correct repository - Important: Without
working_directory, adapters default to current directory (usually the AI Counsel directory) - HTTP Adapter Layer (
adapters/base_http.py,adapters/ollama.py, etc.) - Base:
BaseHTTPAdapterhandles HTTP mechanics, retry logic, error handling - Concrete:
OllamaAdapter,LMStudioAdapter - Retry: exponential backoff on 5xx/429/network errors, fail-fast on 4xx
- Authentication: environment variable substitution
${VAR_NAME}in config - Uses
httpxfor async HTTP,tenacityfor retry - Config Schema (
models/config.py,scripts/migrate_config.py) adapterssection with explicittypefield:cliorhttp- Type discrimination:
CLIAdapterConfigvsHTTPAdapterConfig - Backward compatible with legacy
cli_toolssection - Migration:
python scripts/migrate_config.py config.yaml - Convergence Detection (
deliberation/convergence.py) - Semantic similarity between consecutive rounds
- Backends: SentenceTransformer (best) → TF-IDF → Jaccard (zero deps)
- Statuses: converged (≥85%), refining (40-85%), diverging (<40%), impasse
- Voting-aware: overrides semantic status with voting outcomes
- Structured Voting (
models/schema.py) - Vote: option, confidence (0.0-1.0), rationale, continue_debate flag
- Aggregates into VotingResult with tally and winning option
- Voting overrides semantic similarity: 2-1 vote → "majority_decision", 3-0 → "unanimous_consensus"
- Model-Controlled Early Stopping (
deliberation/engine.py) - Models signal readiness via
continue_debate: falsein votes - Engine checks threshold (default: 66%) after each round
- Respects min_rounds before allowing early stop
- Config:
deliberation.early_stopping.enabled,threshold,respect_min_rounds - Transcript Management (
deliberation/transcript.py) - Generates markdown in
transcripts/directory - Format:
YYYYMMDD_HHMMSS_Question_truncated.md - Includes AI summary, full debate, voting section, tool executions
- Data Models (
models/schema.py) - Pydantic validation:
Participant,DeliberateRequest,RoundResponse,Summary,ConvergenceInfo,DeliberationResult - Voting:
Vote,RoundVote,VotingResult - Type-safe throughout system
- Configuration (
config.yaml) - See
config.yamlfor adapter configs, timeouts, convergence thresholds - Per-CLI command templates with
{model}and{prompt}placeholders - Hook disabling for Claude:
--settings '{"disableAllHooks": true}'
Data Flow
- MCP client invokes
deliberatetool →server.py::call_tool()withworking_directoryset to client's current directory - Request validated against
DeliberateRequestschema DeliberationEngine.execute()orchestrates rounds- For each round:
execute_round()→ prompts enhanced with voting instructions → adapters invoke CLIs withworking_directory- Adapter isolation: Subprocess runs from
working_directory(client's cwd), models analyze correct repository - Responses collected and votes parsed from "VOTE: {json}" markers
- Tool request parsing: Extract TOOL_REQUEST markers from responses
- Tool execution: Change to
working_directory, validate, execute with timeout/error handling, restore original directory - Context injection: Tool results visible to all participants in next round
- Recording: Track in
ToolExecutionRecordfor transcript - Check early stopping: if ≥66% want to stop → break
- After round 2+: convergence detection compares current vs previous round
- If converged/impasse/early-stop: stop early; else continue to max rounds
- Aggregate voting results: determine winner, consensus status
- AI summarizer generates structured summary
- Override convergence status with voting outcome if available
TranscriptManagersaves markdown with "Tool Executions" section- Result serialized and returned to MCP client
MCP Tool Architecture
- MCP-Exposed Tools (callable by MCP clients):
deliberate: Orchestrate multi-round AI deliberation (requiresworking_directoryparameter)query_decisions: Search decision graph memory (when enabled)- Supports
thresholdparameter (0.0-1.0, default 0.6) for adjustable similarity - Empty results include diagnostics:
best_match_score,near_misses,suggested_threshold
- Supports
- Internal Tools (callable by AI models via TOOL_REQUEST):
read_file: Read file contents (max 1MB)search_code: Search codebase with regex patternslist_files: List files matching glob patternsrun_command: Execute safe read-only commands- Internal tools:
TOOL_REQUESTresponse markers → engine parsing/execution → shared results; no direct MCP exposure. - Example:
TOOL_REQUEST: {"name": "read_file", "arguments": {"path": "/path/to/file.py"}} - Why Two Tool Types?
- MCP tools: Clients start deliberations and query results
- Internal tools: AI models gather evidence during deliberation
- Separation keeps MCP interface clean while empowering models with research capabilities
Decision Graph Memory
- The decision graph module (
decision_graph/) enables persistent learning from deliberations. - Module Structure:
schema.py,storage.py,similarity.py,retrieval.py,integration.py,cache.py,workers.py,maintenance.py,query_engine.py,exporters.py - Core Components:
- Schema:
DecisionNode,ParticipantStance,DecisionSimilarity - Storage: SQLite3 with CRUD operations, indexed by timestamp/question_hash/decision_id
- Retrieval: Uses convergence backend, configurable threshold (default: 0.7), max results (default: 3)
- Integration:
store_deliberation(),get_context_for_deliberation(), async background workers - Performance: LRU cache (200 query + 500 embedding entries), 10 jobs/sec, p95 <450ms latency
- Query Engine: Unified interface for search_similar(), find_contradictions(), trace_evolution()
- Export: JSON, GraphML, DOT, Markdown formats
- Data Flow:
- Write: Extract from DeliberationResult → save to SQLite → queue async similarity computation
- Read: Query recent decisions → compute similarity → format as markdown → prepend to Round 1 prompt
- Background: Async worker computes similarities vs recent decisions, stores top-20
- Performance:
- Store: <100ms (async background similarity)
- Query (cache hit): <2μs
- Query (cache miss): <100ms
- Background similarity: <10s per decision
- Memory: ~5KB per decision
- Configuration: See
config.yamlfordecision_graph.enabled,db_path,similarity_threshold,max_context_decisions
Evidence-Based Deliberation
- Enables AI models to gather concrete evidence during debates by executing tools. Tool results visible to ALL participants in subsequent rounds.
- Tool Interface:
BaseToolwith asyncexecute(**kwargs) -> ToolResult, timeout protection, error isolation - Available Tools:
- ReadFileTool: Max 1MB, path validation, descriptive errors
- SearchCodeTool: Uses ripgrep (falls back to grep), output truncation
- ListFilesTool: Glob patterns, recursive search, directory validation
- RunCommandTool: Whitelist (ls, grep, find, cat, head, tail), no shell injection
- Security:
- Input validation: Pydantic schema prevents malformed requests
- Resource limits: Configurable file size (default 1MB), 10s timeout, output truncation
- Path exclusions: Blocks access to configured patterns (
transcripts/,.git/,node_modules/, etc.) to prevent context contamination - Isolation: Tool failures don't halt deliberation
- Audit trail: All executions recorded with timestamps
- Known limitations: No sandboxing beyond path exclusions, no rate limiting
- Performance:
- Parse: <1ms, Validate: <1ms
- read_file: <10ms, search_code: 50-200ms, list_files: <20ms, run_command: 10-100ms
Development Commands
Virtual Environment Setup
python3 -m venv .venv
source .venv/bin/activate # macOS/Linux
pip install -r requirements.txt
pip install -r requirements-optional.txt # Optional: Enhanced convergence backendsTesting
pytest tests/unit -v # Unit tests
pytest tests/integration -v -m integration # Integration tests
pytest tests/e2e -v -m e2e # E2E tests
pytest --cov=. --cov-report=html # Coverage reportCode Quality
black . # Format
ruff check . # Lint
mypy . # Type check (optional)Running the Server
- Development:
python server.py(logs to mcp_server.log and stderr) - Production: Configure in
~/.claude/config/mcp.json, checkmcp_server.logfor debugging
Configuration Notes
Timeouts
- Default: 60s per invocation
- Reasoning models (Claude Opus 5.5, gpt-6.1-sol, o3-pro): 180-300s recommended
- Configure per-CLI in
config.yaml::adapters::<name>::timeout
Model Registry Enabled Field
- Each
ModelDefinitionhas anenabled: boolfield (default:true) - Controls whether models are active and available for deliberations
- Filtering applied automatically in
ModelRegistry.list(),list_for_adapter(),allowed_ids(), andget_default() - Disabled models (
enabled: false) are hidden from MCP clients but definition retained in config - Use cases: cost control, testing, staged rollout, performance tuning, compliance restrictions
- Pattern: Toggle models on/off without deleting configuration, preserving IDs and metadata
Convergence Detection
- Enabled by default:
deliberation.convergence_detection.enabled: true - Threshold: 0.85 (85% similarity = converged)
- Min rounds before checking:
min_rounds_before_check: 1(checks from round 2) - Important: Must be
<= rounds - 1for convergence info. For 2-round deliberations, usemin_rounds_before_check: 1 - Voting override: Voting outcomes override semantic similarity
Model-Controlled Early Stopping
- Enabled:
deliberation.early_stopping.enabled: true - Threshold: 0.66 (66% must want to stop)
- Respects min rounds:
respect_min_rounds: true - Example: 3 models, 2 say
continue_debate: false→ stops (2/3 = 66%)
Reasoning Effort Defaults
- Configure per-adapter reasoning effort defaults in
config.yaml:
cli_tools:
codex:
command: "codex"
args: ["exec", "--model", "{model}", "-c", 'model_reasoning_effort="{reasoning_effort}"', "{prompt}"]
timeout: 180
default_reasoning_effort: "medium" # Config default
droid:
command: "droid"
args: ["exec", "-m", "{model}", "-r", "{reasoning_effort}", "{prompt}"]
timeout: 180
default_reasoning_effort: "medium" # Config default- Priority Chain (highest to lowest):
- Per-participant:
Participant(cli="codex", reasoning_effort="high")→ uses "high" - Config default:
default_reasoning_effort: "medium"→ uses "medium" - Empty string: No default, no per-participant →
{reasoning_effort}becomes""
- Placeholder Mechanism:
{reasoning_effort}placeholder in args substituted at runtime- Codex:
-c model_reasoning_effort=""(empty = default reasoning) - Droid:
-r ""(empty = adapter handles gracefully) - Claude: No placeholder —
--effortflag dynamically injected for supported models (validated via_supports_effort()) - Validation: Invalid values raise
ValueErrorbefore subprocess call
Hook Management
- Claude CLI:
--settings '{"disableAllHooks": true}'prevents hooks from interfering. Critical for reliable execution.
MCP Response Truncation
mcp.max_rounds_in_response: 3limits rounds in MCP response (avoids token limits)- Tool executions are summarized (count by round/type) in MCP response, not full output
- Full transcript with complete tool outputs always saved to file
Extension Guides
- For detailed step-by-step guides on extending the system, see:
- Adding a New CLI Adapter: Subclass
BaseCLIAdapter, implementparse_output(), register in factory - Adding a New HTTP Adapter: Subclass
BaseHTTPAdapter, implementbuild_request()andparse_response(), configure retry logic - Adding a New Tool: Subclass
BaseTool, implementexecute(), register inToolExecutor, update schema, write tests
Design Principles
- DRY: Common logic in base classes, specific parsing in subclasses
- YAGNI: Build only what's needed, no premature optimization
- TDD: Red-green-refactor cycle for all features
- Simple: Straightforward solutions over clever abstractions
- Type Safety: Pydantic validation throughout
- Error Isolation: Adapter failures don't halt entire deliberation
- Configuration:
config.yamlplusmodels/config.pyschemas; no committed TODOs or hardcoded configuration.
Common Gotchas
- Stdio Contamination: Server uses stdio for MCP protocol. All logging MUST go to file or stderr, never stdout.
- Timeout Tuning: Reasoning models can take 60-120+ seconds. Undersized timeouts cause spurious failures.
- Convergence Backend: Optional backends (TF-IDF, SentenceTransformer) improve quality but add dependencies. Zero-dep Jaccard backend always available.
- Model ID Format: Claude CLI uses aliases (
opus,sonnet,haiku), while Droid requires full date-based model IDs (e.g.,claude-opus-4-5-20251101). - Context Building: Previous responses passed as context to subsequent rounds. Large debates = large context. Monitor token usage.
- Async Execution: Engine uses
asynciofor subprocess management. All adapter invocations are async. - Hook Interference: Claude CLI hooks can break CLI invocations during deliberation. Always disable with
--settingsflag. - Prompt Length Limits: Gemini adapter validates prompts ≤100k chars (prevents "invalid argument" API errors).
BaseCLIAdapter.invoke()checksvalidate_prompt_length()if adapter implements it and raisesValueErrorwith helpful message before making API call. Other adapters can implement similar validation. - Tool Execution Errors: Tool failures are isolated - they don't halt deliberation. Models receive error messages in tool results and can adapt their reasoning. Always check
ToolResult.successbefore usingoutput. - Working Directory Requirement: The
deliberatetool requires aworking_directoryparameter (client's current directory). Tools resolve relative paths from this directory. Without it, the request will fail validation. MCP clients should always pass their current working directory. - Database Directory Creation:
DecisionGraphStorageautomatically creates parent directories for the database file if they don't exist. This prevents "readonly database" errors for first-time users. If you get SQLite errors, check file/directory permissions, not just existence. - Tool Path Exclusions: Tools automatically exclude configured patterns (
transcripts/,.git/, etc.) to prevent context contamination. When models read transcript files from previous deliberations about different codebases, they get confused and describe the wrong repository. File tree generation also excludes these directories. - Nested Claude Code Sessions: Base adapter automatically strips the
CLAUDECODEenvironment variable from subprocesses to prevent "nested session" errors when invoking Claude CLI as a deliberation participant from within Claude Code. This is handled transparently inBaseCLIAdapter.invoke(). - NO TODOs - Configuration Pattern Violation: Never commit TODO comments or hardcoded configuration values. This violates the project's configuration architecture where ALL settings live in
config.yamland are validated via Pydantic schemas inmodels/config.py. If you find yourself writing# TODO: Make configurable, STOP immediately and implement proper configuration first. Example violation:max_depth=3 # TODO: Make configurable. Correct approach: AddFileTreeConfigtomodels/config.py, add section toconfig.yaml, read fromself.config.deliberation.file_tree.max_depth. This pattern applies to ALL configurable values across the entire codebase.
Common Development Patterns
Working with Model Registry Enabled Field
# Filter enabled models for an adapter
from models.model_registry import ModelRegistry
registry = ModelRegistry(config)
# Get only enabled models (default behavior)
enabled_models = registry.list_for_adapter("claude")
# Returns: [RegistryEntry(enabled=True), ...]
# Check if model is allowed (respects enabled field)
# Claude CLI: use aliases (opus, sonnet, haiku)
is_allowed = registry.is_allowed("claude", "opus")
# Droid: use full date-based IDs
is_allowed = registry.is_allowed("droid", "claude-opus-4-5-20251101")
# Returns: True only if model exists AND enabled=true
# Get default model (skips disabled models)
default_model_id = registry.get_default("claude")
# Returns: First enabled model marked as default, or first enabled model
# Get all models including disabled (for admin interfaces)
all_models = registry.get_all_models("claude")
# Returns: [RegistryEntry(enabled=True/False), ...]Accessing Decision Graph
# From integration layer
integration = DecisionGraphIntegration()
context = await integration.get_context_for_deliberation(question)
await integration.store_deliberation(question, result)
# From query engine
engine = QueryEngine()
results = await engine.search_similar("question", limit=5)Tool Usage in Deliberations
# Evidence gathering
"Let me check: TOOL_REQUEST: {\"name\": \"read_file\", \"arguments\": {\"path\": \"/file.py\"}}"
# Code search
"I'll search: TOOL_REQUEST: {\"name\": \"search_code\", \"arguments\": {\"pattern\": \"regex\", \"path\": \"/path\"}}"
# Evidence-based voting
"TOOL_REQUEST: {...}\nBased on evidence: VOTE: {\"option\": \"A\", \"confidence\": 0.9, ...}"File System Tool Validation
# Validate paths
if not os.path.exists(path):
return ToolResult(success=False, error=f"Path not found: {path}")
# Check file size
if os.path.getsize(path) > MAX_SIZE:
return ToolResult(success=False, error=f"File too large (max {MAX_SIZE} bytes)")Command Whitelisting
ALLOWED_COMMANDS = {"ls", "grep", "find", "cat", "head", "tail"}
command_name = command.split()[0]
if command_name not in ALLOWED_COMMANDS:
return ToolResult(success=False, error=f"Command not allowed: {command_name}")Async Operations with Timeout
try:
result = await asyncio.wait_for(slow_operation(), timeout=self.timeout)
except asyncio.TimeoutError:
return ToolResult(success=False, error="Operation timed out")Per-Participant Reasoning Effort
from models.schema import Participant, DeliberateRequest
# Override reasoning effort per-participant (config.yaml sets default)
request = DeliberateRequest(
question="Complex architecture question",
participants=[
Participant(cli="codex", model="gpt-6.1-sol", reasoning_effort="high"), # Overrides config default
Participant(cli="claude", model="claude-opus-5-5", reasoning_effort="high"), # Opus supports effort
Participant(cli="claude", model="claude-sonnet-5-5", reasoning_effort="medium"), # Sonnet supports effort
Participant(cli="droid", model="claude-opus-4-5-20251101", reasoning_effort="medium"),
],
rounds=2,
working_directory="/path/to/project",
)
# Adapter support matrix:
# - codex: low, medium, high, xhigh, max, ultra (Astra/Sol only for ultra; injected as -c model_reasoning_effort="...")
# - droid: off, low, medium, high (injected as -r flag)
# - claude: low, medium, high, xhigh, max (current Fable, Opus, Sonnet, and Haiku models)
# - gemini/llamacpp: N/A (reasoning_effort ignored)
# Priority chain (Per-participant > Config default > Empty string):
# 1. Participant(reasoning_effort="high") → uses "high"
# 2. No per-participant + config default_reasoning_effort="medium" → uses "medium"
# 3. No per-participant + no config default → empty string (or None for Claude)Proper Configuration Pattern (NO TODOs)
# WRONG - NEVER DO THIS
def my_function(self):
max_depth = 3 # TODO: Make configurable
max_files = 100 # TODO: Make configurable
# RIGHT - Configuration-First Approach
# 1. Add to models/config.py
class MyFeatureConfig(BaseModel):
max_depth: int = Field(default=3, ge=1, le=10)
max_files: int = Field(default=100, ge=10, le=1000)
class DeliberationConfig(BaseModel):
my_feature: MyFeatureConfig = Field(default_factory=MyFeatureConfig)
# 2. Add to config.yaml
deliberation:
my_feature:
max_depth: 3
max_files: 100
# 3. Use in code
def my_function(self):
config = self.config.deliberation.my_feature
max_depth = config.max_depth
max_files = config.max_filesTesting Strategy
- Unit Tests: Mock adapters, test engine logic, convergence detection, transcript generation, tool execution, schema validation
- Integration Tests: Real CLI invocations (requires tools installed), convergence with real adapters, context injection, concurrent writes, performance benchmarks
- E2E Tests: Full workflow with real API calls (slow, expensive, use sparingly)
- Fixtures:
tests/conftest.pyprovides shared fixtures for adapter mocking - Coverage: 113+ tests, comprehensive coverage across all modules
Development Workflow
- Write test first (TDD)
- Implement feature
- Run unit tests:
pytest tests/unit -v - Format/lint:
black . && ruff check . - Integration test if needed:
pytest tests/integration -v - Update CLAUDE.md if architecture changes
- Commit with clear message
MCP Integration
- This server implements MCP protocol for Claude Code integration:
- Transport: stdio (stdin/stdout)
- Tools:
deliberateandquery_decisionswith structured JSON I/O - Initialization:
mcp.server.stdio.stdio_server() - Error Handling: Errors serialized to JSON with
error_typeandstatusfields
In this repository6 other files it gives its agents.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

