mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-30 08:17:53 +00:00
151 lines
11 KiB
Markdown
151 lines
11 KiB
Markdown
# Development History
|
|
|
|
Summary of the Claude Code sessions that built the engine v2, self-improvement system, and Python orchestrator. This helps new contributors understand *why* things were designed the way they are.
|
|
|
|
## Session 1: Engine v2 Foundation (2026-03-20 to 2026-03-22)
|
|
|
|
Built the core engine crate (`crates/ironclaw_engine/`) from scratch in 6 phases:
|
|
|
|
- **Phase 1**: Core types (Thread, Step, Capability, MemoryDoc, Project), trait definitions (LlmBackend, Store, EffectExecutor), thread state machine. 32 tests.
|
|
- **Phase 2**: Execution engine (Tier 0) — CapabilityRegistry, LeaseManager, PolicyEngine, ThreadManager, ExecutionLoop with structured tool calls. 74 tests.
|
|
- **Phase 3**: CodeAct executor (Tier 1) — Monty Python interpreter integration, RLM pattern (context-as-variables, FINAL(), llm_query(), output truncation, Step 0 orientation). 74 tests.
|
|
- **Phase 4**: Memory and reflection — RetrievalEngine, reflection pipeline (Summary/Lesson/Issue/Spec/Playbook docs), context compaction, rlm_query() recursive sub-agents, budget controls. 78 tests.
|
|
- **Phase 5**: Conversation surface — ConversationManager routing UI messages to threads. 85 tests.
|
|
- **Phase 6**: Bridge adapters — LlmBridgeAdapter, EffectBridgeAdapter, HybridStore, EngineRouter. Parallel deployment via `ENGINE_V2=true`. 151 tests.
|
|
|
|
**Key design decision**: The engine has zero dependency on the main ironclaw crate. All interaction goes through three traits (LlmBackend, Store, EffectExecutor) implemented by bridge adapters.
|
|
|
|
## Session 2: Debugging via Traces (2026-03-22 to 2026-03-23)
|
|
|
|
Ran the engine end-to-end with real LLMs and discovered 8 bugs through trace analysis:
|
|
|
|
1. Tool name hyphens vs underscores (`web-search` vs `web_search`)
|
|
2. Double-serialization of JSON tool output
|
|
3. UTF-8 byte-index slicing panics on multi-byte characters
|
|
4. Code block detection missing in plain completion path
|
|
5. Missing system prompt on thread spawn
|
|
6. Empty messages sent to LLM
|
|
7. `web_fetch` example in prompt (nonexistent tool)
|
|
8. False positive `missing_tool_output` trace warning
|
|
|
|
**Key insight**: Every fix followed the same loop (trace → human reads → human edits Rust → rebuild). This became the motivation for the self-improving engine design.
|
|
|
|
## Session 3: Mission System (2026-03-24)
|
|
|
|
Built the Mission system for long-running goals that spawn threads over time:
|
|
|
|
- `MissionManager` with create/pause/resume/complete lifecycle
|
|
- `MissionCadence`: Cron, OnEvent, OnSystemEvent, Webhook, Manual
|
|
- `build_meta_prompt()` — assembles mission goal + current focus + approach history + project docs + trigger payload
|
|
- `process_mission_outcome()` — extracts next_focus and goal-achieved status from thread responses
|
|
- Cron ticker (60s interval)
|
|
- 7 E2E mission flow tests
|
|
|
|
**Key design decision**: Missions evolve their strategy via `current_focus` and `approach_history`. Each thread gets a meta-prompt that includes what was tried before.
|
|
|
|
## Session 4: Review Fixes + Self-Improvement Foundation (2026-03-25, morning)
|
|
|
|
Fixed 4 review comments (P1/P2 severity) in the engine v2 bridge:
|
|
|
|
1. **SSE events scoped to user** — `broadcast_for_user()` instead of `broadcast()`
|
|
2. **Per-user pending approvals** — HashMap keyed by user_id instead of global Option
|
|
3. **Reset tool-call limit counter** — reset before each thread, not monotonic
|
|
4. **Only auto-approve on "always"** — one-off "yes" no longer persists
|
|
|
|
Then built the self-improvement foundation:
|
|
|
|
- Runtime prompt overlay via MemoryDoc (prompt builder becomes async + Store-aware)
|
|
- `fire_on_system_event()` — wires the previously-unimplemented OnSystemEvent cadence
|
|
- `start_event_listener()` — subscribes to thread events, fires matching missions
|
|
- `ensure_self_improvement_mission()` — creates the built-in self-improvement Mission
|
|
- `process_self_improvement_output()` — saves prompt overlays and fix patterns
|
|
- Seed fix pattern database with 8 known patterns
|
|
|
|
## Session 5: Autoresearch-Inspired Redesign (2026-03-25, afternoon)
|
|
|
|
Studied [karpathy/autoresearch](https://github.com/karpathy/autoresearch) and redesigned the self-improvement approach:
|
|
|
|
**Before**: Vague goal prompt, structured JSON output, reactive only.
|
|
**After**: Concrete `program.md`-style prompt with exact loop steps, plain text + tool-use (agent uses tools directly like autoresearch), enriched trigger payload with actual error messages.
|
|
|
|
Key takeaways applied from autoresearch:
|
|
- The entire "research org" is a markdown prompt with an explicit loop
|
|
- The agent uses tools directly (shell, grep, git) rather than emitting structured output
|
|
- Results tracked in a simple append-only log
|
|
- "NEVER STOP" — the agent is autonomous within constraints
|
|
|
|
## Session 6: Python Orchestrator (2026-03-25, evening)
|
|
|
|
The pivotal architectural change. Motivated by the question: *"What if we move some part of the engine inside CodeAct itself?"*
|
|
|
|
**The realization**: All the bugs from Session 2 were in the "glue" between the LLM and tools — output formatting, tool dispatch, state management, truncation. These functions are Python-natural. If they were Python, the self-improvement Mission could fix them without a Rust rebuild.
|
|
|
|
**Research**: Verified that Monty supports nested VM execution (`rlm_query()` already does exactly this — suspends parent VM, runs child ExecutionLoop, resumes parent). No shared state, ~50KB per suspended VM.
|
|
|
|
**Implementation** (4 commits):
|
|
|
|
1. **Host function module** (`executor/orchestrator.rs`) — 11 host functions exposed to Python via Monty suspension: `__llm_complete__`, `__execute_code_step__`, `__execute_action__`, `__check_signals__`, `__emit_event__`, `__add_message__`, `__save_checkpoint__`, `__transition_to__`, `__retrieve_docs__`, `__check_budget__`, `__get_actions__`.
|
|
|
|
2. **Default orchestrator** (`orchestrator/default.py`) — The v0 Python orchestrator that replicates the Rust loop logic. Helper functions (extract_final, format_output, signals_tool_intent) defined before run_loop for Monty scoping.
|
|
|
|
3. **Switchover** — Replaced the 900-line `ExecutionLoop::run()` with an 80-line bootstrap. Key debugging: Monty's `ExtFunctionResult::NotFound` (not `Error`) for user-defined functions, FINAL result propagation, step_count tracking via `__emit_event__("step_completed")`.
|
|
|
|
4. **Versioning + rollback** — Failure tracking via MemoryDoc, auto-rollback after 3 consecutive failures, `OrchestratorRollback` event. Self-improvement Mission goal updated with Level 1.5 orchestrator patch instructions.
|
|
|
|
**Key debugging moment**: The orchestrator's helper functions (`extract_final`, `format_output`) were defined after `run_loop` in the Python file. Monty couldn't find them because the default `FunctionCall` handler returned `ExtFunctionResult::Error` instead of `ExtFunctionResult::NotFound`. The fix: return `NotFound` for unknown functions so Monty falls through to its own namespace resolution. Then move helpers above `run_loop` to avoid any ordering issues.
|
|
|
|
**Final state**: 189 tests pass, zero clippy warnings. The Python orchestrator is the execution engine. The Rust layer is the kernel.
|
|
|
|
## Session 7: Integration Scaling Research (2026-03-26)
|
|
|
|
Studied [Pica](https://github.com/withoneai/pica) (formerly IntegrationOS, 200+ third-party API integrations) to understand how to rapidly scale the number of available integrations in IronClaw.
|
|
|
|
**Pica's architecture**: Integrations are MongoDB documents, not code. Each platform has a `ConnectionDefinition` (identity + auth schema) and N `ConnectionModelDefinition` records (one per API endpoint: URL, method, auth method, schemas, JS transform functions). A generic executor dispatches requests. OAuth definitions embed JavaScript compute functions executed by a TypeScript service. Adding a new platform = inserting documents, no code changes.
|
|
|
|
**Analysis of IronClaw v1 tools**: Audited all 37 built-in tools. Only 3 (image_gen, image_analyze, image_edit) are HTTP API wrappers. The other 34 are local computation, filesystem, orchestration, or system management — none convertible to data-driven definitions. The value isn't converting existing tools; it's enabling hundreds of new integrations.
|
|
|
|
**Key finding — deterministic executors don't solve the LLM problem**: Even with a Pica-style executor, each integration action must be registered as a tool in the LLM's context. At 200+ tools:
|
|
- ~20,000 tokens always-on cost (tool definitions sent every request)
|
|
- LLM tool selection accuracy degrades beyond ~20-30 tools
|
|
- The LLM still constructs parameters and can get them wrong
|
|
- Deterministic execution only helps *after* the LLM correctly selects the tool and params
|
|
|
|
**The realization**: In engine v2, Capabilities already bundle actions + knowledge. For API integrations, a Capability's knowledge text teaches the LLM how to call the platform's API using the generic `http` action. This is superior to dedicated tools because:
|
|
- Tool list stays small (just `http` + core actions) — high selection accuracy
|
|
- Knowledge loaded on-demand per thread context — zero cost for unused integrations
|
|
- ~350 tokens of knowledge covers 4+ API endpoints (the LLM generalizes)
|
|
- Adding a new platform = writing markdown knowledge, no Rust code
|
|
|
|
**Remaining gap**: OAuth token acquisition requires a dedicated `oauth_init` action (LLM can't do redirect flows). Capability knowledge instructs the LLM to call it before using the API.
|
|
|
|
**Decision**: Use Capabilities as knowledge-bearing integration definitions. Write knowledge text for top 20 platforms. Build one `oauth_init` action. Skip the Pica-style deterministic executor — it solves the wrong problem for LLM agents.
|
|
|
|
## Architecture Evolution
|
|
|
|
```
|
|
Session 1-2: Rust loop (900 lines) → works but bugs in glue layer
|
|
Session 3: + Missions (long-running goals, evolving strategy)
|
|
Session 4: + Self-improvement Mission (fires on issues, fixes prompts)
|
|
Session 5: + Autoresearch-style goal prompt (concrete, not vague)
|
|
Session 6: Rust loop → Python orchestrator (self-modifiable)
|
|
900 lines Rust → 80 lines Rust bootstrap + 230 lines Python
|
|
Session 7: Integration scaling: Capabilities as knowledge → http action
|
|
(not Pica-style per-action tools — tool list bloat kills LLM accuracy)
|
|
```
|
|
|
|
## Key Commits
|
|
|
|
| Commit | Description |
|
|
|--------|-------------|
|
|
| `8be19a4` | Phase 1: Foundation types + traits |
|
|
| `bf7dfb8` | Phase 2: Tier 0 execution engine |
|
|
| `b59a0b9` | Phase 3: CodeAct (Monty + RLM) |
|
|
| `4bc7ffd` | Phase 4: Memory + reflection + budgets |
|
|
| `0827235` | Phase 5: Conversation surface |
|
|
| `ac4ced0` | Phase 6: Bridge adapters (parallel deploy) |
|
|
| `8180a417` | Self-improving engine via Mission system |
|
|
| `cfe856da` | Python orchestrator module + host functions |
|
|
| `63756039` | Switch ExecutionLoop to Python orchestrator |
|
|
| `080317aa` | All 177 tests pass with orchestrator |
|
|
| `46fd2b5d` | Versioning, auto-rollback, 189 tests |
|