mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-07 05:56:41 +02:00
21840c4c67
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>
710 lines
27 KiB
Markdown
710 lines
27 KiB
Markdown
# 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.
|