docs: OpenClaw integration v0 design — two runtimes, one brain

Architecture for gstack + OpenClaw (Wintermute) as complementary runtimes
with shared memory, dispatch protocol, and Clawvisor security gateway.

Key components: generator-native OpenClaw skill output, bidirectional
learnings store, dispatch daemon, session handoff with resume_prompt,
symmetric Clawvisor callback path, JSON Schema contracts for all shared
file formats, weekly activity index rotation, and gstack-bridge skill
for OpenClaw instances.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-04-04 16:08:00 -07:00
parent 846269e3b1
commit 21840c4c67
+709
View File
@@ -0,0 +1,709 @@
# OpenClaw Integration v0 — Two Runtimes, One Brain
## The Opportunity
OpenClaw has 247K GitHub stars — bigger than React. It's the dominant open-source
AI agent framework. It runs always-on, connects to messaging (WhatsApp, Telegram,
Slack, iMessage), manages calendar, memory, and proactive outreach. It's the
"personal AI operating system."
gstack is the deep-work coding agent — spawned per-task, writes artifacts, ships
code, terminates. It doesn't run 24/7. It doesn't manage your calendar.
These aren't competitors. They're complementary runtimes:
- **OpenClaw (Wintermute)**: always-on brain. Messaging, calendar, memory, orchestration.
- **gstack (Claude Code)**: deep-work hands. Coding, review, ship, QA.
The 10/10 version isn't "port gstack skills to OpenClaw format." It's making them
one system with two execution modes and a shared memory/dispatch layer.
## Current State
**PR #491** by @latifclawbot-mdil ported 22 gstack skills to OpenClaw as a thin
compatibility subtree. Each skill is ~15-20 lines, manually maintained, will drift
from templates immediately. Wrong architecture — should be generator-native.
**PR #114** by @dddabtc was an earlier attempt (10 skills). Already closed.
Both approaches treat OpenClaw as a translation target. The right approach treats
it as a co-runtime.
## Architecture
```
┌─────────────────────────────────────────────────┐
│ Wintermute (OpenClaw, always-on) │
│ Messaging. Calendar. Memory. Orchestration. │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Shared Layer │ │
│ │ ~/.gstack/routing.yaml │ │
│ │ ~/.gstack/activity.jsonl │ │
│ │ ~/.gstack/dispatch/{id}.json │ │
│ │ ~/.gstack/handoff/{id}.md │ │
│ │ learnings (bidirectional) │ │
│ └──────────────┬────────────────────────┘ │
│ │ │
│ Creates Clawvisor task for authorization │
└─────────────────┼───────────────────────────────┘
│ HTTPS via ngrok tunnel
┌─────────────────▼───────────────────────────────┐
│ Clawvisor (security gateway, your Mac) │
│ Credential vaulting + task-scoped auth │
│ Human approval for sensitive operations │
│ Chain context verification (anti-injection) │
└─────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ gstack-dispatch-daemon (your Mac) │
│ Spawns Claude Code sessions per task │
│ Monitors progress, writes completion reports │
│ Reports back via Clawvisor callback │
└─────────────────────────────────────────────────┘
```
## Component Design
### 1. Generator-Native OpenClaw Output
Instead of maintaining a separate `openclaw/` subtree, modify `gen-skill-docs.ts`
to emit OpenClaw variants from the same `.tmpl` templates.
New resolver: `scripts/resolvers/openclaw.ts`
**What it does:**
- Replaces Claude tool references with OpenClaw equivalents
(`Read``read`, `Bash``exec`, `$B``browser`)
- Strips Claude-specific preamble (telemetry, upgrade checks, session tracking)
- Injects OpenClaw-native YAML frontmatter (`triggers`, `version`,
`metadata.openclaw.requires`)
- Maps `AskUserQuestion` → "ask in chat reply"
- Maps `Agent` spawning → `sessions_spawn` with appropriate `maxSpawnDepth`
- Outputs to `openclaw/skills/` directory
Every `bun run build` produces both Claude and OpenClaw variants from one source
of truth. Zero drift. Zero manual maintenance.
**Tool mapping table (reference for resolver):**
| gstack (Claude Code) | OpenClaw |
|------------------------|-----------------------|
| Read | `read` |
| Write | `write` |
| Edit | `edit` |
| Bash | `exec` |
| WebSearch | `web_search` |
| browse binary (`$B`) | `browser` or `exec $B`|
| Agent / subagents | `sessions_spawn` |
| AskUserQuestion | chat reply / `message` |
| CLAUDE.md | AGENTS.md |
### 2. SOUL.md — The Builder Identity
OpenClaw's SOUL.md defines persistent agent personality. gstack ships one that
captures the builder ethos:
```markdown
# gstack soul
You are a builder's coding agent. You ship complete implementations, not
shortcuts. You search before building. You prize first-principles thinking
above convention.
## Core principles
- Completeness is cheap with AI coding. Don't recommend shortcuts when the
complete implementation is achievable. Boil the lake.
- Search for built-ins and best practices before designing solutions.
Three layers: tried-and-true, new-and-popular, first-principles.
Prize Layer 3 above all.
- Builder > Optimizer. Ship the thing, then improve it.
## Voice
Direct. Opinionated. No hedging. Lead with the answer, not the reasoning.
Say "do X" not "you might consider X." If you're wrong, be wrong confidently
and correct fast.
## When dispatched by Wintermute
You're the deep-work specialist. Wintermute handles orchestration. You handle
execution. Report back with: what shipped, decisions made, learnings,
and anything that needs human judgment.
```
### 3. Shared Learnings Store (Bidirectional)
**gstack → OpenClaw:**
After each session, write a parallel copy of new learnings as OpenClaw memory:
```
~/.gstack/projects/{slug}/learnings.jsonl (gstack native)
~/.openclaw/workspace/memory/gstack-{slug}.md (OpenClaw readable)
```
Format bridge: each JSONL entry (`pattern`, `context`, `confidence`) becomes a
markdown section in the OpenClaw memory file.
**OpenClaw → gstack:**
Extend `LEARNINGS_SEARCH` in the preamble to also grep
`~/.openclaw/workspace/memory/*.md`. When gstack starts a coding session, it
already knows what Wintermute learned overnight.
**Cost:** ~30 min. Both formats are text files. The bridge is a read adapter.
### 4. Shared Routing Config
A routing table that both runtimes read:
```yaml
# ~/.gstack/routing.yaml
routes:
- match: { project: "baku", files: ["calendar/*"] }
load: ["kitsune-context", "calendar-api-docs"]
- match: { task_type: "security" }
skills: ["gstack-cso", "gstack-careful"]
- match: { task_type: "shipping" }
skills: ["gstack-ship", "gstack-review"]
```
gstack's preamble resolver reads this. OpenClaw's AGENTS.md references it. Same
routing table, two readers. "If touching calendar → load kitsune context" works
in both runtimes.
### 5. Dispatch Protocol (Wintermute → gstack)
The flow when Wintermute dispatches a coding task:
**Step 1: Wintermute creates dispatch file**
```json
{
"id": "dispatch-2026-04-03-auth-flow",
"dispatched_by": "wintermute",
"task": "Build JWT auth flow with refresh token rotation",
"project": "baku",
"project_dir": "~/git/baku",
"learnings": ["...relevant memories from Wintermute..."],
"constraints": ["mobile client needs offline token refresh"],
"callback": "~/.openclaw/inbox/dispatch-{id}.md",
"clawvisor_task_id": "task_abc123",
"ttl_seconds": 3600
}
```
**Step 2: Clawvisor authorization (see section 7)**
**Step 3: gstack-dispatch-daemon spawns session**
```bash
claude --print --permission-mode bypassPermissions \
--project ~/git/baku \
"Load gstack. Dispatch ID: dispatch-2026-04-03-auth-flow. \
Read dispatch context from ~/.gstack/dispatch/dispatch-2026-04-03-auth-flow.json"
```
**Step 4: gstack preamble reads dispatch context**
- Skips AskUserQuestion (non-interactive)
- Pre-loads task description, learnings, constraints
- Runs the task
**Step 5: On completion, writes report**
```markdown
---
dispatch_id: dispatch-2026-04-03-auth-flow
status: completed
duration: 847s
commits: 3
pr: garrytan/baku#47
---
## What shipped
JWT auth flow with refresh token rotation. 3 commits:
1. Schema migration for refresh_tokens table
2. Auth middleware with JWT verification + refresh endpoint
3. Tests: 12 cases covering expiry, rotation, revocation
## Decisions made
- JWT over sessions: mobile client needs offline capability
- 15-min access token TTL, 7-day refresh token
- Refresh rotation: old token invalidated on use (replay protection)
## Learnings logged
- Baku uses Drizzle ORM, not Prisma (discovered from existing schema)
- Test runner is vitest, not jest
## Needs human judgment
- Should refresh tokens survive password change? Currently: no (revoke all).
This is a product decision, not a technical one.
```
**Step 6: Fires callback via Clawvisor (symmetric path)**
Callback flows through the same security layer as dispatch:
```
Dispatch: Wintermute → Clawvisor → Daemon → Claude Code
Callback: Claude Code → Daemon → Clawvisor → Wintermute webhook
```
The daemon POSTs the completion report to the Clawvisor callback URL, which
relays to Wintermute's OpenClaw webhook endpoint. No side-channel
`openclaw system event`. Everything audited both directions.
### 6. Session Handoff (Cross-Runtime Context Transfer)
"I was working on X in Claude Code, continue in OpenClaw" (and vice versa).
**Handoff file format:**
```markdown
---
from: gstack
to: wintermute
project: baku
branch: garrytan/auth-flow
timestamp: 2026-04-03T14:30:00Z
---
## Context
Working on JWT auth flow. Got stuck on refresh token rotation test.
## State
- Branch has 2 commits: schema migration + middleware skeleton
- Tests written but failing on token expiry edge case
- Design doc at ~/.gstack/projects/baku/plans/auth-design.md
## What I need
Debug the refresh token rotation test failure and fix it.
## Resume prompt
Load gstack. Continue work on branch garrytan/auth-flow in ~/git/baku.
Two commits landed (schema + middleware). The refresh token rotation
test is failing on the expiry edge case. Read the test at
test/auth/refresh-rotation.test.ts:47. The expected behavior is that
a rotated refresh token invalidates the old one on first use.
```
The `resume_prompt` field is critical: gstack has full context at checkpoint
time; Wintermute doesn't. When the user says "continue the Baku auth work,"
Wintermute passes the resume prompt straight into the next Claude Code session.
Zero context reconstruction.
Written to `~/.gstack/handoff/{id}.md`. Wintermute reads it. Context transfers
without re-explaining. Extends the existing `/checkpoint` skill.
### 7. Clawvisor Integration (Security Gateway)
Clawvisor (clawhub.ai/ericlevine/clawvisor) sits between Wintermute and your Mac.
It provides credential vaulting, task-scoped authorization, and human approval
flows. The agent never directly handles secrets.
**Why Clawvisor, not raw SSH/ngrok:**
| Raw tunnel | Clawvisor |
|-------------------------------------|---------------------------------------------|
| Full SSH access to Mac | Task-scoped authorization per action |
| No audit trail | Every request has `reason` + `data_origin` |
| One compromise = full access | Token is service-scoped, revokable |
| No human-in-the-loop | Approval required (or auto for trusted scope)|
| Prompt injection → arbitrary cmds | Chain context verification blocks injection |
| All or nothing | Scope expansion requires re-approval |
**Clawvisor service definition for gstack:**
```
Service: gstack-coding
Actions:
spawn-session Start Claude Code + gstack in a project dir
check-status Is the session still running?
get-completion Fetch the completion report
cancel-session Kill a running session
list-sessions What's running + queue depth + max concurrent
```
The `list-sessions` response includes `queue_depth` and `max_concurrent` so
Wintermute can make intelligent dispatch decisions before sending work:
```json
{
"active": 2,
"max_concurrent": 2,
"queued": 1,
"sessions": [
{ "id": "dispatch-auth-flow", "project": "baku", "elapsed": 423 },
{ "id": "dispatch-fix-ci", "project": "gstack", "elapsed": 87 }
]
}
```
When a dispatch arrives and active sessions are at `max_concurrent`, the daemon
queues it (FIFO). Wintermute sees `queued: 1` in `list-sessions` and can decide
whether to wait or cancel a lower-priority session.
**Authorization flow:**
1. Wintermute creates a Clawvisor task declaring scope:
`spawn-session` in `~/git/baku`, read/write in that dir, run tests, create PR
2. Clawvisor checks restrictions (hard blocks on rm -rf /, force-push main, etc.)
3. You get a notification: "Wintermute wants to code in Baku. Approve?"
(or auto-approved if standing task exists for this repo)
4. On approval, dispatch-daemon spawns the session
5. Every action audited with `reason` and `data_origin`
6. On completion, `POST /api/tasks/{id}/complete`
**Standing tasks for trusted workflows:**
```yaml
Standing Task: "gstack-trusted-repos"
scope: [spawn-session, check-status, get-completion]
repos: [baku, gstack, foundation]
restrictions: [no-main-push, no-credential-read, project-dir-only]
auto_execute: true # no approval ping needed
session_id: required # chain context per invocation
```
Wintermute can spawn gstack sessions in approved repos without pinging you.
New repos or destructive actions still require approval.
**Restriction templates (shipped with gstack):**
```yaml
restrictions:
- block: exec
pattern: "rm -rf /"
reason: "never delete root"
- block: exec
pattern: "git push --force origin main"
reason: "no force-push to main"
- block: read
pattern: "~/.ssh/*"
reason: "no credential access"
- block: write
pattern: "*/node_modules/*"
reason: "no writing to dependencies"
```
**Tunnel setup (ngrok or Tailscale):**
```bash
# Option A: ngrok (easy, public URL)
ngrok http $CLAWVISOR_PORT --authtoken $NGROK_TOKEN \
--domain your-stable-domain.ngrok.app
# Option B: Tailscale (better for always-on, private)
tailscale serve --bg $CLAWVISOR_PORT
```
Wintermute's `CLAWVISOR_URL` points to the stable domain.
Authenticated via `CLAWVISOR_AGENT_TOKEN` (minimally scoped).
### 8. gstack-dispatch-daemon
A lightweight Bun process running on your Mac:
```
bin/gstack-dispatch-daemon
Responsibilities:
- Watches ~/.gstack/dispatch/ for new task files
- Validates dispatch came through Clawvisor (token check)
- Spawns `claude --print` with gstack loaded
- Captures stdout, tracks elapsed time, detects hangs
- Writes structured completion report
- Fires callback to Wintermute via Clawvisor
- Manages concurrency (configurable, default: 2 parallel sessions)
- Kills sessions that exceed TTL
```
### 9. Cross-Runtime Diarization (Activity Index)
gstack already produces artifacts that survive on disk: plans, reviews, ship
reports, retros, learnings. The missing piece: a unified activity index that
Wintermute can scan.
```jsonl
// ~/.gstack/activity/activity-2026-W14.jsonl
{"ts":"2026-04-03T14:30:00Z","project":"baku","skill":"ship","artifacts":["pr:47"],"duration":847,"dispatch":"wintermute"}
{"ts":"2026-04-03T12:15:00Z","project":"gstack","skill":"review","artifacts":["review-log.jsonl"],"duration":423}
{"ts":"2026-04-02T16:00:00Z","project":"baku","skill":"investigate","artifacts":["learnings:3"],"duration":612,"dispatch":"wintermute"}
```
**Rotation strategy:** Weekly rollup files (`activity-YYYY-WNN.jsonl`) instead of
a single append-only file. Wintermute answering "what did I ship this week" reads
one file, not 6 months of history. The preamble writes to the current week's file;
old weeks are immutable. A simple `ls ~/.gstack/activity/` gives the full timeline.
Project-specific queries grep across weeks — still fast since each file is small.
### 10. ClawHub Publishing
Package all OpenClaw-variant skills for ClawHub marketplace:
- Versioning synced to gstack's `VERSION` file
- Security metadata: `requires.bins` (gh, git), no env vars by default
- Meta-package: `clawhub install garrytan/gstack` pulls all skills
- CI pipeline: on release tag, auto-publish to ClawHub
- SOUL.md included in the package
This puts gstack in front of OpenClaw's 247K-star user base through their native
package manager.
### 11. Proactive Skill Suggestion Across Runtimes
Wintermute sees "Baku coding session" on calendar → pre-loads context:
1. Reads `~/.gstack/activity.jsonl` for last Baku session
2. Reads `~/.gstack/projects/baku/learnings.jsonl` for project context
3. Checks routing table for Baku-specific skill recommendations
4. Pre-creates dispatch file with context
5. When you say "going to code" → suggests: "Last session was debugging auth.
Want me to dispatch gstack with the checkpoint from yesterday?"
This is orchestration logic on the OpenClaw side. gstack's role is producing the
artifacts that make it possible (which it already does).
### 12. OpenClaw Integration Skill (gstack-bridge)
An actual SKILL.md that OpenClaw instances install — teaches any OpenClaw agent
how to work with gstack. This is the OpenClaw-side counterpart to the gstack
dispatch-aware preamble.
```yaml
---
name: gstack-bridge
description: >
Bridge between OpenClaw and gstack (Claude Code). Dispatch coding tasks,
read completion reports, parse handoffs, query activity timelines, and
manage shared learnings. Install this to make your OpenClaw agent
gstack-aware.
version: 0.1.0
triggers:
- dispatch coding task
- what did I ship
- continue the coding work
- gstack status
metadata:
openclaw:
requires:
bins: [claude, gh, git]
env: [CLAWVISOR_URL, CLAWVISOR_AGENT_TOKEN]
---
```
**What the skill teaches OpenClaw:**
1. **Dispatch protocol** — how to create dispatch files, authorize via Clawvisor,
and spawn Claude Code sessions
2. **Completion parsing** — how to read YAML frontmatter + markdown completion
reports and integrate into memory
3. **Handoff reading** — how to parse handoff files and use `resume_prompt` to
continue work in a new Claude Code session
4. **Activity queries** — how to read `~/.gstack/activity.jsonl` and answer
"what did I ship this week" across both runtimes
5. **Learnings sync** — how to read gstack's JSONL learnings and incorporate
them into OpenClaw memory
6. **Standing task management** — how to configure trusted repos for
auto-approved dispatch
This skill ships to ClawHub alongside all the other gstack skills. Any OpenClaw
user who installs gstack's skill pack gets the bridge automatically.
### 13. JSON Schema Definitions (Shared Contract)
All shared file formats have formal JSON Schema definitions to prevent silent
breakage when either side evolves.
**`schemas/dispatch.schema.json`**
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "dispatched_by", "task", "project", "project_dir"],
"properties": {
"id": { "type": "string", "pattern": "^dispatch-" },
"dispatched_by": { "type": "string" },
"task": { "type": "string", "minLength": 1 },
"project": { "type": "string" },
"project_dir": { "type": "string" },
"learnings": { "type": "array", "items": { "type": "string" } },
"constraints": { "type": "array", "items": { "type": "string" } },
"callback_url": { "type": "string", "format": "uri" },
"clawvisor_task_id": { "type": "string" },
"ttl_seconds": { "type": "integer", "minimum": 60, "maximum": 86400 }
}
}
```
**`schemas/completion.schema.json`**
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["dispatch_id", "status", "duration"],
"properties": {
"dispatch_id": { "type": "string", "pattern": "^dispatch-" },
"status": { "enum": ["completed", "failed", "cancelled", "timeout"] },
"duration": { "type": "integer", "description": "seconds" },
"commits": { "type": "integer" },
"pr": { "type": "string", "description": "owner/repo#number format" },
"error": { "type": "string", "description": "present only on failure" }
}
}
```
**`schemas/handoff.schema.json`**
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["from", "to", "project", "timestamp"],
"properties": {
"from": { "enum": ["gstack", "wintermute"] },
"to": { "enum": ["gstack", "wintermute"] },
"project": { "type": "string" },
"branch": { "type": "string" },
"timestamp": { "type": "string", "format": "date-time" },
"resume_prompt": { "type": "string", "description": "exact prompt to resume session" }
}
}
```
**`schemas/activity-entry.schema.json`**
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["ts", "project", "skill", "duration"],
"properties": {
"ts": { "type": "string", "format": "date-time" },
"project": { "type": "string" },
"skill": { "type": "string" },
"artifacts": { "type": "array", "items": { "type": "string" } },
"duration": { "type": "integer", "description": "seconds" },
"dispatch": { "type": "string", "description": "who dispatched, if any" }
}
}
```
Schemas live in `schemas/` at the repo root. Both the gstack dispatch daemon and
the OpenClaw bridge skill validate against them. Schema version is tied to gstack
VERSION — breaking changes require a major version bump.
### 14. Version Compatibility
OpenClaw-variant skills carry `min_openclaw_version` in frontmatter:
```yaml
---
name: gstack-ship
version: 0.15.2.0
min_openclaw_version: "1.8.0"
---
```
OpenClaw's skill loader checks this and warns if the runtime is too old.
Prevents "why isn't this working" support issues on ClawHub.
The gstack-bridge skill additionally carries `min_gstack_version` to ensure
the dispatch daemon and file formats are compatible:
```yaml
min_gstack_version: "0.16.0.0"
```
## Resolved Decisions
1. **Browse binary on OpenClaw:** Ship standalone, invoke via `exec $B`. Don't
adapter it. The browse binary's capabilities (snapshot diffing, chain commands,
cookie import, annotated screenshots) are years ahead of OpenClaw's native
browser. Wrapping it would lose features.
2. **Multi-agent orchestration:** Ship and measure. OpenClaw's `sessions_spawn`
doesn't share prompt cache like Claude Code's Agent tool, so sub-agents will
be slower and more expensive. Worth shipping and measuring the cost delta
rather than deferring.
3. **Concurrency on dispatch:** Default 2 parallel sessions. Bottleneck is
Anthropic rate limits, not local compute. Configurable in
`~/.gstack/config.yaml` via `dispatch.max_concurrent`.
4. **Standing task scope:** `trusted_repos` list in `~/.gstack/config.yaml`.
Start with personal repos (`garrytan/*` auto-approves). Org repos or other
people's repos require per-dispatch approval.
5. **PR #491 disposition:** Close it. Generator-native approach replaces the
entire subtree. Credit @latifclawbot-mdil for tool mapping research in
CHANGELOG.
## Implementation Plan
### Phase 1: Generator + ClawHub (distribution)
- OpenClaw resolver in `gen-skill-docs.ts`
- Add OpenClaw frontmatter to all skill templates (including `min_openclaw_version`)
- SOUL.md
- ClawHub publishing pipeline
- **Result:** gstack skills installable on OpenClaw via `clawhub install`
### Phase 2: Shared Layer (memory bridge)
- JSON Schema definitions for all shared file formats
- Bidirectional learnings adapter
- Activity index (session diarization)
- Shared routing config
- Extend `/checkpoint` for cross-runtime handoff (with `resume_prompt`)
- **Result:** both runtimes share memory and context with validated contracts
### Phase 3: Remote Dispatch (Wintermute → gstack)
- Dispatch file format + protocol (validated against schema)
- Dispatch-aware preamble
- gstack-dispatch-daemon (symmetric Clawvisor callback path)
- Clawvisor service adapter + restriction templates
- ngrok/Tailscale tunnel setup
- gstack-bridge skill for OpenClaw (teaches Wintermute the full protocol)
- **Result:** Wintermute can autonomously dispatch coding sessions
## Effort Estimate
| Component | Time |
|---------------------------------------------------|---------|
| OpenClaw resolver in gen-skill-docs | 30 min |
| SOUL.md + AGENTS.md template | 15 min |
| ClawHub CI publishing pipeline | 30 min |
| JSON Schema definitions (4 schemas) | 30 min |
| Bidirectional learnings adapter | 30 min |
| Activity index | 15 min |
| Shared routing config | 30 min |
| Dispatch protocol + file format | 30 min |
| Dispatch-aware preamble | 30 min |
| gstack-dispatch-daemon (with symmetric callback) | 1 hour |
| Clawvisor service adapter + restrictions | 45 min |
| ngrok/Tailscale setup integration | 15 min |
| gstack-bridge skill for OpenClaw | 45 min |
| **Total** | **~7h** |
## Resolved Decisions (from Wintermute review)
6. **Offline Mac:** Fail fast, let Wintermute decide retry strategy. No
Clawvisor queuing (keeps the security layer stateless). No iCloud/Dropbox
sync (conflict resolution nightmare). But Clawvisor error responses MUST
distinguish failure modes so Wintermute can retry intelligently:
- `mac_asleep` — tunnel unreachable, retry when Mac wakes
- `tunnel_down` — ngrok/Tailscale not running, user action needed
- `daemon_crashed` — dispatch daemon not responding, restart needed
- `queue_full` — all slots occupied, check `list-sessions` for ETAs
7. **Schema evolution:** Additive-only for minor versions, strict semver for
breaking. **Breaking change defined as:** removing a required field,
changing a field's type, or changing the semantics of an existing field.
**Non-breaking:** adding optional fields. This definition goes in a
`COMPATIBILITY.md` at the repo root so future contributors don't guess
under pressure.
8. **Activity index rotation:** Weekly rollup files
(`activity-YYYY-WNN.jsonl`) instead of single append-only file. See
section 9 for details.
9. **Dispatch queueing:** When active sessions are at `max_concurrent`, the
daemon queues incoming dispatches (FIFO). `list-sessions` returns
`queue_depth` so Wintermute can decide whether to wait or cancel a
lower-priority session. See section 7 for the response format.
10. **Multiple OpenClaw agents:** Don't over-engineer. The `dispatched_by`
field already handles identity. If multi-agent becomes real, add
per-agent config sections in `~/.gstack/config.yaml`. Flag it, move on.