Spaces:
Sleeping
Sleeping
| # Research: OpenClaw Integration | |
| **Feature**: 001-openclaw-integration | **Date**: 2026-02-27 | |
| ## R-001: OpenClaw JSONL Transcript Format | |
| **Decision**: The parser must handle the nested OpenClaw JSONL event format, not flat role/content events. | |
| **Rationale**: Inspecting the sample transcript (`b3db607f-...jsonl`) reveals the format is significantly different from what the current parser assumes: | |
| - **Top-level fields**: `type`, `id`, `parentId`, `timestamp`, and optionally `message` | |
| - **Event types**: `"session"`, `"message"`, `"thinking_level_change"`, `"custom"` | |
| - **Message structure**: `event["message"]` contains `role`, `content` (array), `api`, `provider`, `model`, `usage` | |
| - **Content array items**: Each has a `type` field β `"text"`, `"thinking"`, `"toolCall"`, `"toolResult"` | |
| - Text: `{"type": "text", "text": "..."}` | |
| - Thinking: `{"type": "thinking", "thinking": "...", "thinkingSignature": "reasoning_content"}` | |
| - Tool call: `{"type": "toolCall", "id": "...", "name": "...", "arguments": {...}}` | |
| - Tool result: `{"type": "toolResult", "toolCallId": "...", "content": [{"type": "text", "text": "..."}]}` | |
| **Current parser bug**: `parse_session_jsonl()` reads `event.get("role")` and `event.get("content")` at the top level. The actual data has `event["message"]["role"]` and `event["message"]["content"]` (an array, not a string). The current parser will always produce empty traces from real OpenClaw sessions. | |
| **Alternatives considered**: None β must match the actual format. | |
| ## R-002: Trace Dict Structure for TraceAnalyser | |
| **Decision**: Keep the existing trace dict format `{question, reasoning, answer, skill_ids, feedback, ground_truth}` as TraceAnalyser accepts raw dicts placed on `ctx.trace`. | |
| **Rationale**: `TraceAnalyser` uses raw traces β any dict type is accepted and placed on `ctx.trace`. The Reflector then reads `ctx.trace` (or falls back to `ctx.agent_output`). The trace dict keys should map cleanly to what Reflector's prompt template expects. Existing test patterns confirm this structure works. | |
| **Alternatives considered**: Using `AgentOutput` dataclass β rejected because TraceAnalyser explicitly supports raw traces without wrapping. | |
| ## R-003: Thinking Content Handling | |
| **Decision**: Preserve thinking content in full β no truncation or filtering. | |
| **Rationale**: Thinking traces are integral to how the OpenClaw agent works. They contain the richest reasoning data and provide essential context for the ACE learning pipeline to understand the agent's decision-making process. Truncation would lose critical information that the Reflector needs to extract meaningful strategies. | |
| **Clarification**: User explicitly confirmed thinking traces must be preserved in full (see spec Clarifications 2026-02-27). | |
| **Alternatives rejected**: | |
| - Head+tail truncation (500/200 chars) β rejected per user requirement; loses critical reasoning context. | |
| - Filtering thinking blocks entirely β rejected; removes the most valuable data for learning. | |
| ## R-004: Tool Call/Result Pairing | |
| **Decision**: Extract tool calls with their names and arguments from `"toolCall"` content items, and pair with results from subsequent `"toolResult"` events. Preserve all data in full β no truncation of arguments or results. | |
| **Rationale**: Tool usage is a key signal for the Reflector. The JSONL format stores tool calls as content items within assistant messages, and tool results as separate content items (or messages) with matching `toolCallId`. Pairing them gives the Reflector a complete picture of what tools were used and what they returned. | |
| **Clarification**: User explicitly confirmed tool arguments and results must be preserved in full (see spec Clarifications 2026-02-27). | |
| **Alternatives rejected**: Ignoring tool calls β rejected because tool usage patterns are one of the most valuable things to learn from. Truncating tool results β rejected per user requirement. | |
| ## R-005: Session Discovery Path | |
| **Decision**: Discover sessions from `~/.openclaw/agents/<agent_id>/sessions/*.jsonl` with the session directory structure matching the JSONL sample. | |
| **Rationale**: The sample file's `"session"` event contains `"cwd": "/app"` and session metadata. OpenClaw stores sessions per-agent under the home directory. The `OPENCLAW_AGENT_ID` environment variable selects which agent's sessions to process. | |
| **Alternatives considered**: Recursive glob for all agents β rejected for simplicity (Principle III). Users can run the script multiple times with different `OPENCLAW_AGENT_ID` values. | |
| ## R-006: Skillbook Persistence Format | |
| **Decision**: Use the existing `Skillbook.save_to_file()` / `Skillbook.load_from_file()` JSON format. No custom serialization needed. | |
| **Rationale**: The skillbook JSON format is well-defined with fields: `skills`, `sections`, `next_id`, `similarity_decisions`. Each skill has `id`, `section`, `content`, `justification`, `evidence`, `helpful/harmful/neutral`, timestamps, and `status`. The existing API handles all serialization. | |
| **Alternatives considered**: None β reusing existing infrastructure per Principle III. | |
| ## R-007: AGENTS.md Sync Format | |
| **Decision**: Use `wrap_skillbook_context()` from `ace.integrations` to format the skillbook content between HTML comment markers `<!-- ACE:SKILLBOOK:START/END -->`. | |
| **Rationale**: `wrap_skillbook_context()` already produces a formatted string with skillbook strategies and usage instructions. The marker-based replacement pattern in `sync_to_agents_md()` is correct β preserves content outside markers, handles create/update cases. | |
| **Alternatives considered**: Custom formatting β rejected because `wrap_skillbook_context()` already exists and produces the right format. | |
| ## R-008: Error Handling Strategy | |
| **Decision**: Graceful degradation β skip individual malformed sessions, report errors, continue processing. | |
| **Rationale**: Constitution Principle I (Ease of Use) requires that a single bad file doesn't crash the entire run. FR-010 explicitly requires skipping malformed files gracefully. The current try/except pattern in `parse_session_jsonl()` handles `JSONDecodeError` correctly. | |
| **Alternatives considered**: Strict mode that fails on first error β rejected as it violates FR-010 and Principle I. | |
| ## R-009: Testing Strategy | |
| **Decision**: Unit tests with MockLLMClient, using sample JSONL fixtures. No live LLM calls in tests. | |
| **Rationale**: Existing test patterns in `tests/` use `MockLLMClient` that returns canned `ReflectorOutput` and `SkillManagerOutput`. This allows testing the full flow (parse β analyse β save β sync) without API costs or flakiness. The sample JSONL file can serve as a test fixture. | |
| **Alternatives considered**: Integration tests with real LLM β useful but should be `@pytest.mark.slow` and optional. | |