* refactor: restructure CLAUDE.md into modular rules and add pr-shepherd command Trim CLAUDE.md from 710 lines to 92 by moving detailed guidance into path-scoped `.claude/rules/` files that load on demand. Add a new `/pr-shepherd` command that consolidates the full PR lifecycle (review, fix, quality gate, CI fix loop, merge) into one workflow. Changes: - CLAUDE.md: keep only essentials (build commands, code style, architecture, module specs, config reference, debugging) - .claude/rules/review-discipline.md: 15+ review rules, scoped to src/**/*.rs - .claude/rules/database.md: dual-backend rules with SQL dialect translation table, scoped to src/db/** and migrations/** - .claude/rules/safety-and-sandbox.md: safety layer and sandbox rules, scoped to src/safety/**, src/sandbox/**, src/secrets/** - .claude/rules/testing.md: test tiers and patterns, scoped to src/** and tests/** - .claude/rules/tools.md: tool architecture and implementation pattern, scoped to src/tools/** and tools-src/** - .claude/commands/pr-shepherd.md: 7-phase PR lifecycle command that subsumes review-pr, respond-pr, ship, and manual CI fix loops [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address review feedback on CLAUDE.md restructure - Restore project structure tree in CLAUDE.md (zmanian blocking) - Create .claude/rules/skills.md with trust model, SKILL.md format, selection pipeline, and skill tools (zmanian blocking) - Restore configuration section with key env vars (zmanian medium) - Restore "Adding a New Channel" guide (zmanian medium) - Add heartbeat mention to Workspace & Memory section (zmanian low) - Fix pr-shepherd: replace `git add -A` with specific file staging (zmanian) - Fix pr-shepherd: ask user for merge strategy instead of hardcoding --squash (zmanian) - Fix pr-shepherd: replace `--watch` with polling + 10min timeout (zmanian) - Fix testing.md: "skipped if DB is unreachable" not "expected to fail" (Copilot) [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address review comments on PR #750 - Narrow `crate::` import rule: `super::` is fine in tests and intra-module refs - Fix capabilities file naming: `<name>.capabilities.json` sidecar, not bare `capabilities.json` - Update mechanical verification checklist to match narrowed import rule Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: move Bedrock docs from CLAUDE.md to src/llm/CLAUDE.md Bedrock provider details (auth, config, feature flag) belong in the LLM module spec, not the top-level guide. Added file map entry, provider table row, and dedicated section in src/llm/CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: move env var config block out of CLAUDE.md Replace 20-line config block with one-liner pointing to .env.example and src/llm/CLAUDE.md. Config details are only needed during deployment, not everyday coding. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: use gh pr checkout for fork-safe PR checkout in pr-shepherd Replaces git fetch/checkout with gh pr checkout {number} which handles both same-repo and fork-based PRs automatically. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address Copilot review round 5 on PR #750 - Add gh pr list and gh pr checkout to pr-shepherd allowed-tools - Align crate:: import rule in pr-shepherd with updated CLAUDE.md guidance - Fix vector type in database.md: BLOB (flexible dims), not F32_BLOB(1536) - Update MCP limitation: stdio/HTTP/Unix transports exist, no streaming Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
11 KiB
IronClaw Development Guide
IronClaw is a secure personal AI assistant — user-first security, self-expanding tools, defense in depth, multi-channel access with proactive background execution.
Build & Test
cargo fmt # format
cargo clippy --all --benches --tests --examples --all-features # lint (zero warnings)
cargo test # unit tests
cargo test --features integration # + PostgreSQL tests
RUST_LOG=ironclaw=debug cargo run # run with logging
E2E tests: see tests/e2e/CLAUDE.md.
Code Style
- Prefer
crate::for cross-module imports;super::is fine in tests and intra-module refs - No
pub usere-exports unless exposing to downstream consumers - No
.unwrap()or.expect()in production code (tests are fine) - Use
thiserrorfor error types inerror.rs - Map errors with context:
.map_err(|e| SomeError::Variant { reason: e.to_string() })? - Prefer strong types over strings (enums, newtypes)
- Keep functions focused, extract helpers when logic is reused
- Comments for non-obvious logic only
Architecture
Prefer generic/extensible architectures over hardcoding specific integrations. Ask clarifying questions about the desired abstraction level before implementing.
Key traits for extensibility: Database, Channel, Tool, LlmProvider, SuccessEvaluator, EmbeddingProvider, NetworkPolicyDecider, Hook, Observer, Tunnel.
All I/O is async with tokio. Use Arc<T> for shared state, RwLock for concurrent access.
Project Structure
src/
├── lib.rs # Library root, module declarations
├── main.rs # Entry point, CLI args, startup
├── app.rs # App startup orchestration (channel wiring, DB init)
├── bootstrap.rs # Base directory resolution (~/.ironclaw), early .env loading
├── settings.rs # User settings persistence (~/.ironclaw/settings.json)
├── service.rs # OS service management (launchd/systemd daemon install)
├── tracing_fmt.rs # Custom tracing formatter
├── util.rs # Shared utilities
├── config/ # Configuration from env vars (split by subsystem)
│ ├── mod.rs # Re-exports all config types; top-level Config struct
│ ├── agent.rs, llm.rs, channels.rs, database.rs, sandbox.rs, skills.rs
│ ├── heartbeat.rs, routines.rs, safety.rs, embeddings.rs, wasm.rs
│ ├── tunnel.rs # Tunnel provider config (TUNNEL_PROVIDER, TUNNEL_URL, etc.)
│ └── secrets.rs, hygiene.rs, builder.rs, helpers.rs
├── error.rs # Error types (thiserror)
│
├── agent/ # Core agent loop, dispatcher, scheduler, sessions — see src/agent/CLAUDE.md
│
├── channels/ # Multi-channel input
│ ├── channel.rs # Channel trait, IncomingMessage, OutgoingResponse
│ ├── manager.rs # ChannelManager merges streams
│ ├── cli/ # Full TUI with Ratatui
│ ├── http.rs # HTTP webhook (axum) with secret validation
│ ├── webhook_server.rs # Unified HTTP server composing all webhook routes
│ ├── repl.rs # Simple REPL (for testing)
│ ├── web/ # Web gateway (browser UI) — see src/channels/web/CLAUDE.md
│ └── wasm/ # WASM channel runtime
│
├── cli/ # CLI subcommands (clap)
│ ├── mod.rs # Cli struct, Command enum (run/onboard/config/tool/registry/mcp/memory/pairing/service/doctor/status/completion)
│ └── config.rs, tool.rs, registry.rs, mcp.rs, memory.rs, pairing.rs, service.rs, doctor.rs, status.rs, completion.rs
│
├── registry/ # Extension registry catalog
│ ├── manifest.rs # ExtensionManifest, ArtifactSpec, BundleDefinition types
│ ├── catalog.rs # RegistryCatalog: load from filesystem and embedded JSON
│ └── installer.rs # RegistryInstaller: download, verify, install WASM artifacts
│
├── hooks/ # Lifecycle hooks (6 points: BeforeInbound, BeforeToolCall, BeforeOutbound, OnSessionStart, OnSessionEnd, TransformResponse)
│
├── tunnel/ # Tunnel abstraction (cloudflare, ngrok, tailscale, custom, none)
│
├── observability/ # Pluggable event/metric recording (noop, log, multi)
│
├── orchestrator/ # Internal HTTP API for sandbox containers
│ ├── api.rs # Axum endpoints (LLM proxy, events, prompts)
│ ├── auth.rs # Per-job bearer token store
│ └── job_manager.rs # Container lifecycle (create, stop, cleanup)
│
├── worker/ # Runs inside Docker containers
│ ├── runtime.rs # Worker execution loop (tool calls, LLM)
│ ├── claude_bridge.rs # Claude Code bridge (spawns claude CLI)
│ └── proxy_llm.rs # LlmProvider that proxies through orchestrator
│
├── safety/ # Prompt injection defense
│ ├── sanitizer.rs # Pattern detection, content escaping
│ ├── validator.rs # Input validation (length, encoding, patterns)
│ ├── policy.rs # PolicyRule system with severity/actions
│ ├── leak_detector.rs # Secret detection (API keys, tokens, etc.)
│ └── credential_detect.rs # HTTP request credential detection
│
├── llm/ # Multi-provider LLM integration — see src/llm/CLAUDE.md
│
├── tools/ # Extensible tool system
│ ├── tool.rs # Tool trait, ToolOutput, ToolError
│ ├── registry.rs # ToolRegistry for discovery
│ ├── rate_limiter.rs # Shared sliding-window rate limiter
│ ├── builtin/ # Built-in tools (echo, time, json, http, web_fetch, file, shell, memory, message, job, routine, extension_tools, skill_tools, secrets_tools)
│ ├── builder/ # Dynamic tool building
│ ├── mcp/ # Model Context Protocol client
│ └── wasm/ # Full WASM sandbox (wasmtime) — runtime, host functions, fuel metering, allowlist, credential injection
│
├── db/ # Dual-backend persistence (PostgreSQL + libSQL) — see src/db/CLAUDE.md
│
├── workspace/ # Persistent memory system — see src/workspace/README.md
│
├── context/ # Job context isolation (JobState, JobContext, ContextManager)
├── estimation/ # Cost/time/value estimation with EMA learning
├── evaluation/ # Success evaluation (rule-based, LLM-based)
│
├── sandbox/ # Docker execution sandbox
│ ├── config.rs # SandboxConfig, SandboxPolicy enum (ReadOnly/WorkspaceWrite/FullAccess)
│ ├── manager.rs # SandboxManager orchestration
│ ├── container.rs # ContainerRunner, Docker lifecycle
│ └── proxy/ # Network proxy: domain allowlist, credential injection, CONNECT tunnel
│
├── secrets/ # Secrets management (AES-256-GCM, OS keychain for master key)
│
├── setup/ # 7-step onboarding wizard — see src/setup/README.md
│
├── skills/ # SKILL.md prompt extension system — see .claude/rules/skills.md
│
└── history/ # Persistence (PostgreSQL repositories, analytics)
tests/
├── *.rs # Integration tests (workspace, heartbeat, WS gateway, pairing, etc.)
├── test-pages/ # HTML→Markdown conversion fixtures
└── e2e/ # Python/Playwright E2E scenarios (see tests/e2e/CLAUDE.md)
Database
Dual-backend: PostgreSQL + libSQL/Turso. All new persistence features must support both backends. See src/db/CLAUDE.md and .claude/rules/database.md.
Module Specs
When modifying a module with a spec, read the spec first. Code follows spec; spec is the tiebreaker.
| Module | Spec |
|---|---|
src/agent/ |
src/agent/CLAUDE.md |
src/channels/web/ |
src/channels/web/CLAUDE.md |
src/db/ |
src/db/CLAUDE.md |
src/llm/ |
src/llm/CLAUDE.md |
src/setup/ |
src/setup/README.md |
src/tools/ |
src/tools/README.md |
src/workspace/ |
src/workspace/README.md |
tests/e2e/ |
tests/e2e/CLAUDE.md |
Job State Machine
Pending -> InProgress -> Completed -> Submitted -> Accepted
\-> Failed
\-> Stuck -> InProgress (recovery)
\-> Failed
Skills System
SKILL.md files extend the agent's prompt with domain-specific instructions. See .claude/rules/skills.md for full details.
- Trust model: Trusted (user-placed in
~/.ironclaw/skills/or workspaceskills/, full tool access) vs Installed (registry, read-only tools) - Selection pipeline: gating (check bin/env/config requirements) -> scoring (keywords/patterns/tags) -> budget (fit within
SKILLS_MAX_TOKENS) -> attenuation (trust-based tool ceiling) - Skill tools:
skill_list,skill_search,skill_install,skill_remove
Configuration
See .env.example for all environment variables. LLM backends (nearai, openai, anthropic, ollama, openai_compatible, tinfoil, bedrock) documented in src/llm/CLAUDE.md.
Adding a New Channel
- Create
src/channels/my_channel.rs - Implement the
Channeltrait - Add config in
src/config/channels.rs - Wire up in
src/app.rschannel setup section
Workspace & Memory
Persistent memory with hybrid search (FTS + vector via RRF). Four tools: memory_search, memory_write, memory_read, memory_tree. Identity files (AGENTS.md, SOUL.md, USER.md, IDENTITY.md) injected into system prompt. Heartbeat system runs proactive periodic execution (default: 30 minutes), reading HEARTBEAT.md and notifying via channel if findings. See src/workspace/README.md.
Debugging
RUST_LOG=ironclaw=trace cargo run # verbose
RUST_LOG=ironclaw::agent=debug cargo run # agent module only
RUST_LOG=ironclaw=debug,tower_http=debug cargo run # + HTTP request logging
Current Limitations
- Domain-specific tools (
marketplace.rs,restaurant.rs, etc.) are stubs - Integration tests need testcontainers for PostgreSQL
- MCP: no streaming support; stdio/HTTP/Unix transports all use request-response
- WIT bindgen: auto-extract tool schema from WASM is stubbed
- Built tools get empty capabilities; need UX for granting access
- No tool versioning or rollback
- Observability: only
logandnoopbackends (no OpenTelemetry)