| name | e2e |
| description | Run end-to-end smoke tests for the Mycelium stack. Verifies install, memory, search, coordination, and OpenClaw integration. Use when validating a release, after a deploy, or when something feels broken. |
| argument-hint | [--full | --quick | --openclaw] |
End-to-End Testing
Run structured smoke tests against the live Mycelium stack. Tests are cumulative — each phase depends on the previous one passing.
Arguments
--quick — Stack health + memory CRUD + search only (< 1 min)
--full — Quick + CLI negotiation to consensus (~ 3 min)
--openclaw — Full + OpenClaw agent wake/respond test (~ 5 min, requires gateway running)
- No argument — defaults to
--full
Phase 1: Stack Health
Verify all services are running and healthy.
curl -sf http://localhost:8000/health | python3 -m json.tool
docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "mycelium|ioc"
curl -sf http://localhost:9000/health
docker inspect ioc-cognition-fabric-node-svc --format '{{.State.Health.Status}}'
Fail criteria: Any service unhealthy → stop and diagnose. Do not proceed.
Phase 2: Memory CRUD + Search
Test the core memory pipeline: write, read, list, search, delete.
mycelium room create e2e-test-room --trigger threshold:10
mycelium room use e2e-test-room
mycelium memory set decisions/test-db "Chose Postgres for reliability" -H e2e-agent
mycelium memory set decisions/test-cache "Redis for session caching" -H e2e-agent
mycelium memory set failed/test-sqlite "SQLite can't handle concurrent writes" -H e2e-agent
mycelium memory set status/test-deploy "Staging deploy in progress" -H e2e-agent
mycelium memory get decisions/test-db
mycelium memory ls
mycelium memory ls decisions/
mycelium memory search "what database did we pick"
mycelium memory search "what failed"
mycelium memory rm decisions/test-cache --force
mycelium memory ls
ls ~/.mycelium/rooms/e2e-test-room/decisions/
cat ~/.mycelium/rooms/e2e-test-room/decisions/test-db.md
Fail criteria: Any write/read/search fails → embedding or DB issue.
Phase 3: CLI Negotiation
Test the full coordination pipeline: session create → join → tick → respond → consensus.
For cold-spawn families (cursor, claude_code) the operator no longer needs to drive an accept loop. The daemon polls /api/coordination-sessions every 5s, dynamically subscribes to each active session sub-room's SSE stream, and on every coordination_tick cold-spawns the owned agent with a formatted instruction. The agent then runs mycelium negotiate respond accept|reject|counter_offer … itself. CognitiveEngine drives rounds until consensus or a 20-round timeout. (For openclaw, the long-lived gateway plugin handles the same wakeup; see Phase 5.)
mycelium session create -r e2e-test-room
mycelium session join --handle agent-alpha -m "Prioritize performance" -r e2e-test-room
mycelium session join --handle agent-beta -m "Prioritize developer experience" -r e2e-test-room
SESSION_ID=$(mycelium session ls -r e2e-test-room --json 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])" 2>/dev/null)
curl -s "http://localhost:8000/api/coordination-sessions" \
| python3 -c "import sys,json,os
sid=os.environ.get('SESSION_ID')
for s in json.load(sys.stdin):
if s['id']==sid: print(s['state']); break"
mycelium --json room messages "e2e-test-room:session:<short_id>" --type coordination_consensus --limit 200 \
| python3 -c "import sys,json
for m in json.load(sys.stdin)['messages']:
print(m['content']); break"
Manual override (operator playing an agent role, e.g. when only one side is daemon-owned, debugging, or simulating a non-mycelium counterparty):
mycelium session await --handle agent-alpha -r e2e-test-room
mycelium negotiate respond accept --room e2e-test-room --handle agent-alpha
Fail criteria:
- No ticks after 60s → CFN not configured or join timer didn't fire
- Ticks arrive but no
dispatch @<handle> ← CognitiveEngine in the daemon log → handle not in daemon.toml.handles, or daemon doesn't own it on this host (sibling daemon owns it instead)
dispatch fires but spawn exits 1 with credit / auth errors → adapter LLM provider not funded or not authed (this is what the autonomous flow exposes that the operator-driven loop used to mask). Top up credits, re-auth, or route through a different provider before retrying.
- Counter_offer_not_your_turn loops in the room → agent ignored the per-round permission set in the tick payload; an agent-side prompt issue, not coordination
broken: true in consensus → 20-round budget elapsed without mutual accept → typically opposing personas locked on incompatible positions
_expand_slim DB session leak → check pg_stat_activity for idle-in-transaction
Phase 4: Multi-Session (same room)
Verify a second negotiation can run in a room after the first completes.
mycelium session create -r e2e-test-room
mycelium session join --handle agent-gamma -m "Ship fast" -r e2e-test-room
mycelium session join --handle agent-delta -m "Ship safe" -r e2e-test-room
Fail criteria:
session create returns the old completed session → _spawn_session_room not filtering completed state
- CFN start fails → stale Session rows not cleaned up (check
_finish_cfn)
Phase 5: OpenClaw Integration
Test that OpenClaw agents get woken by coordination ticks and respond autonomously.
Prerequisites: OpenClaw gateway running, mycelium adapter installed, agents able to exec mycelium CLI. Two ways to achieve this per agent in openclaw.json:
- Option A (simpler):
"sandbox": {"mode": "off"}
- Option B (preserves container isolation):
"tools": {"exec": {"host": "gateway"}} — routes exec to the gateway host where mycelium is installed while keeping sandbox isolation for read/write/edit.
openclaw gateway status
grep "mycelium.*Ready" /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log | tail -1
mycelium room create e2e-openclaw-test
mycelium session create -r e2e-openclaw-test
openclaw agent --agent julia-agent --session-id e2e-oc-1 \
-m "Run: mycelium session join --handle julia-agent --room e2e-openclaw-test -m 'Position A'" \
--timeout 60 &
openclaw agent --agent selina-agent --session-id e2e-oc-2 \
-m "Run: mycelium session join --handle selina-agent --room e2e-openclaw-test -m 'Position B'" \
--timeout 60 &
sleep 50
grep "mycelium.*wake dispatched\|mycelium.*wake completed" /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log | tail -10
mycelium room messages "e2e-openclaw-test:session:<short_id>" --limit 50
Fail criteria:
wake dispatched but no wake completed → openclaw CLI not on PATH or agent auth broken
wake completed but no agent messages in session → agent ran but didn't execute mycelium command (check agent model/skill)
Plugin runtime subagent methods are only available during a gateway request → old plugin installed, needs mycelium adapter add openclaw --reinstall
- SSE errors with
Failed to parse URL → getApiUrl() returning empty, check ~/.mycelium/config.toml
Phase 5.5: Knowledge Extraction Hook (PENDING — not yet tested)
Test that the mycelium-knowledge-extract OpenClaw hook correctly ships conversation turns to the backend and that the backend's two-stage LLM extraction writes memories into the room.
Prerequisites: OpenClaw running with the mycelium-knowledge-extract hook installed, an agent session that has completed at least one turn, ~/.mycelium/config.toml with valid workspace_id and mas_id.
ls ~/.openclaw/hooks/mycelium-knowledge-extract/handler.js
ls ~/.openclaw/mycelium-extract-state/
curl -sf -X POST http://localhost:8001/api/knowledge/ingest \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "<WORKSPACE_ID from ~/.mycelium/.env>",
"mas_id": "<MAS_ID from room>",
"agent_id": "e2e-agent",
"records": [{
"schema": "openclaw-conversation-v1",
"extractedAt": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"session": {"agentId": "e2e-agent", "sessionId": "e2e-test-1", "channel": "default", "cwd": "/tmp"},
"stats": {"totalEntries": 2, "turns": 1, "toolCallCount": 0, "thinkingTurnCount": 0, "totalCost": 0},
"turns": [{
"index": 0,
"timestamp": null,
"model": "claude-sonnet-4-6",
"stopReason": "end_turn",
"usage": null,
"userMessage": "What is the best way to cache database queries?",
"thinking": null,
"toolCalls": [],
"response": "Use Redis with a TTL — set keys per query hash, expire after 5 minutes."
}]
}]
}' | python3 -m json.tool
mycelium memory ls
Fail criteria:
- 503 from
/api/knowledge/ingest → LLM auth failure (check LLM_MODEL and key in .env)
- 200 but no memories written →
IngestionService.ingest extraction returned empty results; check backend logs
- Hook fires but logs fallback entries →
getIngestTarget() can't resolve apiUrl/workspaceId/masId; check ~/.mycelium/config.toml
- Hook never fires → check OpenClaw hook registration (
openclaw hooks list)
TODO: Determine what memory keys IngestionService writes and add assertions above.
Cleanup
curl -s -X DELETE http://localhost:8000/api/rooms/e2e-test-room
curl -s -X DELETE http://localhost:8000/api/rooms/e2e-openclaw-test
Interpreting Failures
| Symptom | Likely cause | Check |
|---|
| Backend returns 500 on memory write | Embedding model not loaded | docker logs mycelium-backend | grep embed |
| Search returns empty | Embeddings are null (wrote with --no-embed) | Reindex: mycelium memory reindex |
| Ticks never arrive | CFN not configured on room | curl rooms/{room} → check mas_id/workspace_id |
| Ticks arrive but agents don't respond | OpenClaw plugin using old subagent.run() | Reinstall adapter |
| Consensus has empty assignments | CFN response envelope not normalized | Check _normalize_cfn_decide_response |
| Second session reuses completed room | Session cleanup bug | Check _spawn_session_room state filter |
| Backend hangs after a few rounds | _expand_slim DB session leak | Check for idle-in-transaction in pg_stat_activity |