A minimal reference implementation showing how to build a self-evolving agent system with marrow-core.
marrow-core is a lightweight agent scheduler with hard isolation between the core (human-maintained) and agent workspace (agent-maintained). Agents evolve freely within their workspace; core changes require a human proposal review.
your-machine/
├── /opt/marrow-core/ # root-owned — immutable scheduler core
└── /Users/you/ # agent-owned — your agent evolves here
├── context.d/ # context scripts (agent-readable sensors)
├── .opencode/agents/ # agent persona definitions
├── tasks/queue/ # human → agent task inbox
└── runtime/ # state, handoffs, checkpoints, logs
Two agents run on a heartbeat:
| Agent | Interval | Role |
|---|---|---|
| scout | 5 min | Fast dispatcher — reads task queue, does trivial work, delegates complex tasks |
| artisan | 4 h | Deep worker — picks the highest-value task, completes it end-to-end with checkpoints |
- macOS 13+ (Linux support via systemd also available in marrow-core)
- opencode installed:
npm i -g opencode-ai - Python 3.12+
- An LLM API key configured in opencode (Anthropic / OpenAI / etc.)
# Install into a dedicated venv at /opt/marrow-core
sudo python3 -m venv /opt/marrow-core/.venv
sudo /opt/marrow-core/.venv/bin/pip install marrow-coregit clone https://github.com/marrow-bot/hello-marrow /Users/$(whoami)/hello-marrow
cd /Users/$(whoami)/hello-marrowUpdate the workspace paths to match your home directory:
# marrow.toml
core_dir = "/opt/marrow-core"
[[agents]]
name = "scout"
heartbeat_interval = 300
heartbeat_timeout = 300
workspace = "/Users/YOUR_NAME" # ← update this
agent_command = "opencode run --agent scout"
context_dirs = ["/Users/YOUR_NAME/context.d"] # ← update this
[[agents]]
name = "artisan"
heartbeat_interval = 14400
heartbeat_timeout = 7200
workspace = "/Users/YOUR_NAME" # ← update this
agent_command = "opencode run --agent artisan"
context_dirs = ["/Users/YOUR_NAME/context.d"] # ← update this/opt/marrow-core/.venv/bin/marrow validate --config marrow.tomlOne-shot (great for testing):
/opt/marrow-core/.venv/bin/marrow run-once --config marrow.tomlPersistent daemon:
/opt/marrow-core/.venv/bin/marrow run --config marrow.tomlInstall as macOS launchd service (runs automatically at login):
/opt/marrow-core/.venv/bin/marrow install-service --config marrow.tomlDrop a plain text file into tasks/queue/:
echo "Research the top 5 async Python frameworks and write a comparison to docs/async-frameworks.md" \
> tasks/queue/$(date +%s)_research-task.txtScout picks it up within 5 minutes, does any trivial parts, and delegates complex work to Artisan.
All communication is via the filesystem — no sockets, no databases:
tasks/queue/ ← human drops tasks here
tasks/done/ ← completed tasks moved here
runtime/handoff/scout-to-artisan/ ← scout delegates complex tasks
runtime/handoff/artisan-to-scout/ ← artisan offloads quick work
runtime/state/artisan-todo.json ← artisan's persistent TODO across sessions
runtime/checkpoints/ ← artisan writes checkpoints every 20-30 min
runtime/logs/exec/ ← per-session stdout/stderr logs
Agent personas live in .opencode/agents/. You can edit them freely — they're yours.
scout.md — controls the 5-minute fast loop (what the scout scans, how it prioritises, what it delegates)
artisan.md — controls the 4-hour deep work session (how Artisan plans, researches, checkpoints)
Custom agents can be added as .opencode/agents/custom-*.md.
Everything in context.d/ is run before each agent tick. The stdout is injected into the agent prompt. This lets you give agents real-time sensor data:
# context.d/00_queue.py
# Shows the agent what tasks are waiting
import os
from pathlib import Path
queue = Path(os.environ.get("WORKSPACE", ".")) / "tasks" / "queue"
tasks = sorted(queue.glob("*")) if queue.is_dir() else []
if tasks:
print(f"TASK QUEUE ({len(tasks)} items):")
for t in tasks[:10]:
print(f" - {t.name}")
else:
print("TASK QUEUE: empty")marrow-core heartbeat (scheduler)
│
├── 5 min ──► scout fast dispatch; trivial tasks; delegate complex work
└── 4 h ──► artisan deep work + research; end-to-end tasks with checkpoints
Core boundary (hard isolation):
/opt/marrow-core/ ← root-owned, immutable to the agent
/Users/you/ ← agent-owned, evolves freely
Core changes flow:
agent writes proposal → tasks/queue/core-proposal-*.md → human reviews
The isolation is enforced by filesystem permissions: core is owned by root, workspace is owned by the agent user. The agent literally cannot modify core — Permission denied.
- marrow-core — the scheduler itself
- AGENTS.md — full architecture reference
- marrow-bot.github.io — live marrow agent dashboard
MIT