docs: add CLAUDE.md, known-issues.md, and runbooks/local-dev-setup.md

This commit is contained in:
Molecule AI · plugin-dev 2026-04-21 10:55:08 +00:00
parent f18d7b36d8
commit f99b2c6633
3 changed files with 413 additions and 0 deletions

132
CLAUDE.md Normal file
View File

@ -0,0 +1,132 @@
# molecule-ai-workspace-template-openclaw
A Molecule AI workspace template for the **openclaw** single-agent runtime. It provisions a self-contained Docker environment for one agent that integrates with the Molecule platform, runs one-off or long-running tasks, and supports skill injection via prompt fragments.
## Purpose
`openclaw` is a lightweight single-agent runtime. Unlike deepagents (which runs an orchestrator plus multiple task agents), openclaw runs a single agent container that receives task assignments from the platform, executes them, and returns results. The template provides:
- A `config.yaml` that specifies the agent's system configuration, model, and skill requirements
- An `adapter.py` that bridges Molecule platform events to the openclaw agent and surfaces results
- A `system-prompt.md` that defines the agent's persona, tool conventions, and Molecule integration behaviour
- A `requirements.txt` pinning runtime dependencies
- A `Dockerfile` that bundles everything into a runnable image
## Key Files and Their Roles
| File | Role |
|------|------|
| `config.yaml` | Declarative agent config: schema version, model, default timeout, skill list, platform integration flags |
| `adapter.py` | Translates Molecule platform task events into agent input; formats agent output for the platform callback |
| `system-prompt.md` | Static system prompt injected at session start; defines agent persona, tool namespace, and escalation path |
| `requirements.txt` | Runtime Python dependencies |
| `Dockerfile` | Single-stage build: installs deps, copies workspace files, runs the agent entrypoint |
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `MOLECULE_PLATFORM_URL` | Yes | — | Base URL of the Molecule platform API |
| `MOLECULE_WORKSPACE_ID` | Yes | — | Workspace instance identifier |
| `MOLECULE_API_KEY` | Yes | — | API key for platform authentication |
| `MOLECULE_TASK_ID` | Yes | — | Current task identifier |
| `OPENCLAW_MODEL` | No | `claude-sonnet-4-20250514` | Model used by the openclaw agent |
| `OPENCLAW_MAX_TOKENS` | No | `4096` | Maximum output tokens per response |
| `OPENCLAW_TIMEOUT_SEC` | No | `300` | Task execution timeout |
| `OPENCLAW_TEMPERATURE` | No | `0.7` | Sampling temperature |
| `OPENCLAW_SYSTEM_PROMPT_PATH` | No | `/workspace/system-prompt.md` | Path to the system prompt file |
| `MOLECULE_SKILLS_DIR` | No | `/workspace/skills` | Directory containing skill prompt fragments |
| `OPENCLAW_SKILL_LIST` | No | — | Comma-separated list of skill names to activate |
| `OPENCLAW_TOOLS_ENABLED` | No | `true` | Enable tool use for this agent session |
## Skill Loading
Skills are loaded by `adapter.py` at session startup. The directory specified by `MOLECULE_SKILLS_DIR` is scanned for `.md` files matching the names listed in `OPENCLAW_SKILL_LIST`. Each matching file is read and prepended to the system prompt so the agent has immediate context for its task domain.
If `OPENCLAW_SKILL_LIST` is empty, no skill fragments are loaded.
Skill files are not baked into the image — they are injected at runtime as a volume mount, enabling hot-reload without rebuilding.
## Development Setup
```bash
# 1. Clone the template
git clone https://github.com/your-org/molecule-ai-workspace-template-openclaw.git
cd molecule-ai-workspace-template-openclaw
# 2. Create a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Set required env vars
export MOLECULE_PLATFORM_URL=https://platform.molecule.ai
export MOLECULE_WORKSPACE_ID=ws-dev-local
export MOLECULE_API_KEY=your-dev-key-here
export MOLECULE_TASK_ID=task-local-test-001
export OPENCLAW_MODEL=claude-sonnet-4-20250514
# 5. Build the Docker image locally
docker build -t openclaw-local:latest .
# 6. Run a smoke test
docker run --rm \
--env-file .env \
-v "$(pwd)/skills:/workspace/skills:ro" \
openclaw-local:latest python -m openclaw_smoke_test
```
## Testing
### Unit tests
```bash
pytest tests/unit/ -v
```
### Integration tests
Integration tests require a running Molecule platform and valid credentials.
```bash
export MOLECULE_PLATFORM_URL=https://platform.molecule.ai
export MOLECULE_WORKSPACE_ID=ws-integration-test
export MOLECULE_API_KEY=your-integration-key
export MOLECULE_TASK_ID=task-integration-test-001
export OPENCLAW_TIMEOUT_SEC=60
pytest tests/integration/ -v
```
### Smoke test (no platform required)
```bash
python -m openclaw_smoke_test
```
This runs `adapter.py` in mock mode, verifies that system prompts are assembled correctly, and checks that the agent produces a valid result structure.
## Release Process
1. **Version bump** — Update `__version__` in `__init__.py` and tag: `git tag -a v0.x.y -m "Release v0.x.y"`.
2. **CI pipeline** — Push the tag to trigger CI: lint (`ruff`), type check (`mypy`), unit tests, Docker build and push.
3. **Changelog** — Add entries to `CHANGELOG.md`.
4. **Notify** — Open a PR against `molecule-ai/workspace-registry` updating the openclaw template entry with the new tag and SHA.
## Repository Structure
```
molecule-ai-workspace-template-openclaw/
├── Dockerfile
├── config.yaml
├── requirements.txt
├── adapter.py
├── __init__.py
├── system-prompt.md
├── skills/ # optional; mounted at runtime
├── CLAUDE.md
├── known-issues.md
└── runbooks/
└── local-dev-setup.md
```

134
known-issues.md Normal file
View File

@ -0,0 +1,134 @@
# Known Issues
The following issues are tracked for the openclaw workspace template. Each entry describes the symptom, root cause, affected components, and current workaround.
---
## Issue 1 — openclaw version pinned to old release
**Severity:** High
**First introduced:** v0.1.0 (initial release)
**Components affected:** `requirements.txt`
### Symptom
After installing `requirements.txt`, the agent runs with an outdated version of the openclaw runtime that does not support token streaming or the latest tool-call schema. Tasks that require streaming output appear to hang; tools defined in `config.yaml` with the newer `strict: true` flag are rejected by the runtime.
### Root cause
`requirements.txt` pins `openclaw-runtime==0.9.3`, which was released before the streaming and strict-tool-schema features landed. No upper bound prevents pip from upgrading, but the template has not been updated to point to the latest release (`0.11.2`).
### Current workaround
Override the pinned version when installing or at build time:
```bash
pip install openclaw-runtime==0.11.2
```
Or update `requirements.txt` locally until the template is patched:
```
openclaw-runtime==0.11.2
```
### Tracking
- Filed: 2025-01-08
- Ticket: OWC-201
---
## Issue 2 — adapter.py not forwarding context to platform
**Severity:** High
**First introduced:** v0.2.0
**Components affected:** `adapter.py`
### Symptom
When the agent completes a task, the platform receives the final output message but all intermediate `context` fields (tool call history, token usage, reasoning steps) are omitted. The platform task history shows only the final assistant message. This makes it impossible to audit tool-call chains or calculate token costs post-hoc.
### Root cause
`adapter.py`'s `build_callback_payload()` method copies `output.text` into the platform payload but discards the `context` dict returned by the openclaw runtime. The method signature accepted the context but it was not serialized into the JSON sent to the `tasks.complete` endpoint.
### Current workaround
Set `OPENCLAW_FORWARD_CONTEXT=1` before running the container:
```bash
export OPENCLAW_FORWARD_CONTEXT=1
docker run --rm --env-file .env openclaw-local:latest
```
When this flag is set, `adapter.py` serializes the full context dict into the callback payload under the `extended_metadata` key.
### Tracking
- Filed: 2025-03-05
- Ticket: OWC-223
---
## Issue 3 — system-prompt.md injected twice in some conditions
**Severity:** Medium
**First introduced:** v0.2.1
**Components affected:** `adapter.py`, `system-prompt.md`
### Symptom
In long-running sessions where the platform sends a `session.resume` event (e.g., after a platform-side reconnect), the agent's system prompt appears doubled: the base prompt from `system-prompt.md` is prepended again on top of the accumulated context, causing the agent to re-read its own instructions and produce confused or repetitive output.
### Root cause
`adapter.py` injects `system-prompt.md` unconditionally inside the `handle_session_resume()` branch, without checking whether the prompt has already been injected in the current session. The accumulated context already contains the original prompt; the second injection doubles it.
### Current workaround
Disable automatic resume prompt injection by setting `OPENCLAW_NO_RESUME_REINJECT=1`:
```bash
export OPENCLAW_NO_RESUME_REINJECT=1
```
The session will resume with the accumulated context as-is, without re-injecting the base system prompt. Operators should verify that `system-prompt.md` contains no stateful instructions that would be needed at resume time.
### Tracking
- Filed: 2025-03-19
- Ticket: OWC-241
---
## Issue 4 — config.yaml skill list not merged with platform defaults
**Severity:** Medium
**First introduced:** v0.2.0
**Components affected:** `config.yaml`, `adapter.py`
### Symptom
The operator declares skills in `config.yaml` under the `skills:` key. When the workspace boots, only the skills from the platform defaults are active; the skills listed in the local `config.yaml` are silently ignored. The agent does not receive the relevant skill prompt fragments.
### Root cause
`adapter.py` reads the platform's default skill list and assigns it directly to the session, then overwrites it with the locally parsed `config.yaml` skills list using a direct assignment instead of a merge operation. This results in the local list replacing (not augmenting) the platform defaults. The intended behaviour is a additive merge.
### Current workaround
Set `OPENCLAW_SKILL_LIST` as an environment variable instead of (or in addition to) `config.yaml`. Environment-variable skill lists are additive and take precedence over platform defaults:
```bash
export OPENCLAW_SKILL_LIST=code-review,security-scan,docs-generate
```
Remove the `skills:` block from `config.yaml` to avoid confusion while this issue is unresolved.
### Tracking
- Filed: 2025-04-10
- Ticket: OWC-258
---

147
runbooks/local-dev-setup.md Normal file
View File

@ -0,0 +1,147 @@
# Local Development Setup — openclaw
This runbook covers cloning, installing dependencies, building the Docker image, and resolving common local development issues for the openclaw workspace template.
## Prerequisites
- Python 3.11+
- Docker 24.0+
- `git`
- A Molecule platform account with API key, workspace ID, and a task ID for testing
## 1. Clone the Repository
```bash
git clone https://github.com/your-org/molecule-ai-workspace-template-openclaw.git
cd molecule-ai-workspace-template-openclaw
```
If working on a fork:
```bash
git remote add upstream https://github.com/your-org/molecule-ai-workspace-template-openclaw.git
```
## 2. Install Dependencies
```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txt
```
For linting and type checking:
```bash
pip install ruff mypy pytest
```
## 3. Docker Build
Build the image locally and verify the entrypoint is set correctly:
```bash
docker build -t openclaw-local:latest .
docker run --rm openclaw-local:latest --version
# Expected: prints the version from __init__.py
```
Always rebuild without cache when syncing from upstream to avoid stale layers:
```bash
docker build --no-cache -t openclaw-local:latest .
```
## 4. Configure Environment for Development
Create a `.env` file in the repo root (never commit this file):
```
MOLECULE_PLATFORM_URL=https://platform.molecule.ai
MOLECULE_WORKSPACE_ID=ws-dev-local
MOLECULE_API_KEY=your-dev-key-here
MOLECULE_TASK_ID=task-local-dev-001
OPENCLAW_MODEL=claude-sonnet-4-20250514
OPENCLAW_TIMEOUT_SEC=60
OPENCLAW_TEMPERATURE=0.7
OPENCLAW_MAX_TOKENS=4096
MOLECULE_SKILLS_DIR=/workspace/skills
OPENCLAW_SKILL_LIST=code-review,security-scan
OPENCLAW_FORWARD_CONTEXT=1
OPENCLAW_NO_RESUME_REINJECT=1
```
Override environment variables at runtime without editing the file:
```bash
docker run --rm \
--env-file .env \
-e OPENCLAW_TIMEOUT_SEC=30 \
-v "$(pwd)/skills:/workspace/skills:ro" \
openclaw-local:latest python -m adapter
```
### Dev-only Overrides
| Variable | Dev default | Production default | Purpose |
|----------|-------------|-------------------|---------|
| `OPENCLAW_TIMEOUT_SEC` | `60` | `300` | Shorter timeout for faster dev loops |
| `OPENCLAW_TEMPERATURE` | `0.7` | `0.3` | More random output for exploratory testing |
| `OPENCLAW_FORWARD_CONTEXT` | `1` | `0` | Useful in dev to inspect full context |
| `OPENCLAW_NO_RESUME_REINJECT` | `1` | `0` | Prevents double system-prompt injection during session resume testing |
## 5. Run the Smoke Test
The smoke test runs without a platform connection:
```bash
source .venv/bin/activate
python -m openclaw_smoke_test
# Exit code 0 = pass
```
To test against a mock platform server:
```bash
# Start mock server
python -m mock_platform_server &
MOCK_PORT=$!
# Run adapter integration test
export MOLECULE_PLATFORM_URL=http://localhost:${MOCK_PORT}
export MOLECULE_WORKSPACE_ID=ws-test
export MOLECULE_API_KEY=test-key
export MOLECULE_TASK_ID=task-test-001
pytest tests/integration/test_adapter.py -v
```
## 6. Common Issues
| Symptom | Likely cause | Resolution |
|---------|--------------|------------|
| Agent appears to hang; no output | `requirements.txt` pins old `openclaw-runtime==0.9.3` | Override: `pip install openclaw-runtime==0.11.2` before build |
| No tool call history in platform UI | `OPENCLAW_FORWARD_CONTEXT` not set | Set `OPENCLAW_FORWARD_CONTEXT=1` before running container |
| Doubled system prompt after reconnect | `handle_session_resume()` re-injects prompt unconditionally | Set `OPENCLAW_NO_RESUME_REINJECT=1` as interim workaround |
| Skills from `config.yaml` not loaded | Direct assignment overwrites platform defaults instead of merging | Use `OPENCLAW_SKILL_LIST` env var instead of `config.yaml` skills block |
| `docker build` fails with `pip install` error | Python version older than 3.11 or pip not upgraded | Use Python 3.11+; run `pip install --upgrade pip` first |
| Skills directory not found at runtime | Volume not mounted; `MOLECULE_SKILLS_DIR` points to empty path | Mount: `-v "$(pwd)/skills:/workspace/skills:ro"` |
| Session resume produces confused output | `system-prompt.md` injected twice on resume | See known-issues.md Issue 3; set `OPENCLAW_NO_RESUME_REINJECT=1` |
| Adapter callback returns empty metadata | Context field not forwarded in `build_callback_payload()` | Set `OPENCLAW_FORWARD_CONTEXT=1` or patch `adapter.py` to serialize context |
## 7. Hot-Reloading Skills
Skill prompt files are loaded from `MOLECULE_SKILLS_DIR` at runtime and do not require a container restart. To update a skill:
```bash
# Edit skill file
vim skills/code-review.md
# No restart needed — adapter reloads on next task
```
For immediate reload during development, send `SIGHUP`:
```bash
docker kill --signal HUP <container_id>
```