From 436bfa71853e43af07f3f1ecf82e316b39f0696a Mon Sep 17 00:00:00 2001 From: uxwolt Date: Sat, 4 Apr 2026 15:27:18 +0000 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20IWCL=20dispatch=20protocol=20?= =?UTF-8?q?=E2=80=94=20inter-wolt=20communication=20skill=20+=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /woltspace-dispatch skill with step-by-step protocol for raccoon orchestrators to dispatch work to other wolts via tmux send-keys. Includes pre-flight checklist, message format, monitoring commands, worker selection guide, and troubleshooting. Also adds docs/iwcl.md with architecture overview and future roadmap (inbox model, bidirectional status, autonomous orchestration). Based on learnings from Session 31 — first successful multi-wolt parallel build (2 features, 2 workers, 2 PRs in ~20 minutes). Co-Authored-By: Claude Opus 4.6 --- container/skills/woltspace-dispatch/SKILL.md | 145 +++++++++++++++++++ docs/iwcl.md | 89 ++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 container/skills/woltspace-dispatch/SKILL.md create mode 100644 docs/iwcl.md diff --git a/container/skills/woltspace-dispatch/SKILL.md b/container/skills/woltspace-dispatch/SKILL.md new file mode 100644 index 0000000..a5bb888 --- /dev/null +++ b/container/skills/woltspace-dispatch/SKILL.md @@ -0,0 +1,145 @@ +# Dispatch — IWCL Work Orchestration + +Dispatch work to other wolts via IWCL (Inter-Wolt Communication Layer). +Use this when you have a well-defined task that another wolt should build. + +## When to Use + +- You have a spec or clear task description +- The work is better suited to another creature type (beaver for building, otter for quick tasks) +- You want to parallelize — dispatch to multiple wolts simultaneously +- You're a raccoon orchestrating a multi-feature build + +## Pre-flight Checklist + +Before spawning a worker, verify: + +```bash +# 1. Check creds exist and aren't stale +ls -la /workspace/wolts/{WOLT}/.claude/.credentials.json +# Should be a real file (not symlink), 400+ bytes + +# 2. Check if wolt has active sessions (proven auth) +tmux list-sessions 2>/dev/null | grep {WOLT} + +# 3. Check system load — 4+ opus processes = OOM risk +ps aux | grep claude | wc -l +``` + +If creds are missing or stale, copy from shared: +```bash +cp /workspace/wolts/.claude/.credentials.json /workspace/wolts/{WOLT}/.claude/.credentials.json +``` + +## Dispatch Flow + +### Step 1: Spawn the worker session + +```bash +curl -s -X POST http://localhost:7777/sessions/new/lodge \ + -H "Content-Type: application/json" \ + -d '{"wolt": "WOLT_NAME"}' +``` + +Save the returned `name` — this is the tmux session name. + +### Step 2: Wait for boot (45-60 seconds) + +```bash +# Check if they got past login and finished /start-chat +tmux capture-pane -t SESSION_NAME -p -S -20 | tail -10 +# Should show the wolt's greeting and a prompt (❯) +``` + +### Step 3: Send the spec + +```bash +tmux send-keys -t SESSION_NAME "Your full task description here. Include: +1. What to build (clear spec) +2. Files to change (with descriptions) +3. Dev workflow (clone URL, branch name, test commands) +4. Constraints (don't edit production, commit often)" Enter +``` + +**Important:** Send one message with everything. Multi-message dispatch risks the wolt +starting after the first message before seeing the full spec. + +**Important:** The text may paste but not submit. Always verify with `tmux capture-pane` +that the wolt started working (not still showing `[Pasted text #1 +N lines]`). +If stuck, send an extra `tmux send-keys -t SESSION_NAME Enter`. + +### Step 4: Monitor progress + +```bash +# What's on screen +tmux capture-pane -t SESSION_NAME -p -S -30 + +# Did they clone / make changes +git -C /workspace/wolts/projects/CLONE_DIR/ diff --stat + +# Check commits +git -C /workspace/wolts/projects/CLONE_DIR/ log --oneline -5 +``` + +### Step 5: Course-correct if needed + +```bash +tmux send-keys -t SESSION_NAME "Correction: also update X because Y" Enter +``` + +### Step 6: Review and push + +When the worker is done, review the diff: +```bash +git -C /workspace/wolts/projects/CLONE_DIR/ show --stat HEAD +git -C /workspace/wolts/projects/CLONE_DIR/ diff HEAD~1 +``` + +Tell them to push: +```bash +tmux send-keys -t SESSION_NAME "Looks good. Push it. Use gh-app-token for auth. Push to BRANCH on jerpint/woltspace. Clean up the remote URL after." Enter +``` + +### Step 7: Create the PR + +```bash +export GH_TOKEN=$(gh-app-token) +gh pr create --repo jerpint/woltspace --head BRANCH --base main \ + --title "feat: description" \ + --body "Summary + test plan + IWCL note (who built it)" +``` + +## Worker Selection + +| Task Type | Best Creature | Why | +|-----------|--------------|-----| +| Platform code, multi-file | Raccoon (opus) | Subtle, needs deep context | +| Contained feature build | Beaver (sonnet) | Fast, reliable, follows specs | +| Quick fix, one file | Otter (haiku) | Cheap, fast | +| UX review, design | Raccoon (opus) | Taste and judgment | + +## Dev Workflow Template + +Include this in every dispatch message: + +``` +DEV WORKFLOW: +- Clone: git clone https://github.com/jerpint/woltspace.git /workspace/wolts/projects/FEATURE/ +- cd /workspace/wolts/projects/FEATURE/ +- Branch: git checkout -b uxw/FEATURE +- Test: uv run --project server --with pytest pytest test/ +- Commit often. Do NOT edit /workspace/woltspace/ — that's production. +- Push with gh-app-token when ready. +``` + +## Troubleshooting + +**Wolt stuck at login screen:** Stale credentials. Kill the session, copy fresh creds, respawn. + +**Message pasted but not submitted:** Send `tmux send-keys -t SESSION Enter` to submit. + +**Wolt ignores the spec:** It may have been in the middle of its own boot flow. Wait for the +`❯` prompt before sending. + +**OOM kills:** Too many opus processes. Check `ps aux | grep claude | wc -l`. Kill idle sessions +before spawning new ones. diff --git a/docs/iwcl.md b/docs/iwcl.md new file mode 100644 index 0000000..35d99d5 --- /dev/null +++ b/docs/iwcl.md @@ -0,0 +1,89 @@ +# IWCL — Inter-Wolt Communication Layer + +IWCL lets wolts communicate with each other. Currently uses `tmux send-keys` to type messages +into running Claude Code sessions. Simple, reliable, no new infrastructure needed. + +## How It Works + +``` +Orchestrator (raccoon) + │ + ├── spawn worker via API ──→ POST /sessions/new/lodge + │ + ├── send spec via tmux ────→ tmux send-keys -t SESSION "spec" Enter + │ + ├── monitor progress ──────→ tmux capture-pane + filesystem checks + │ + └── review + push ─────────→ git diff, then tell worker to push +``` + +The orchestrator (typically a raccoon) breaks work into tasks, spawns worker wolts, +dispatches specs, monitors progress, and reviews output. + +## Message Transport + +**Current:** `tmux send-keys -t SESSION_NAME -l "message"` + `Enter` + +This types text directly into the wolt's Claude Code session as if a human typed it. +The wolt receives it as a normal user message. + +**Constraints:** +- Target session must be a live tmux session +- Claude must be at a prompt (not in the middle of a tool call) +- Long messages work but may trigger pasting mode +- No authentication — any wolt can message any other wolt + +## Dispatch Protocol + +See the `/woltspace-dispatch` skill for the full step-by-step protocol. + +Quick version: +1. Write a spec +2. Verify worker's credentials (pre-flight check) +3. Spawn worker session via API +4. Wait for boot (~60 seconds) +5. Send spec via tmux send-keys +6. Monitor with capture-pane and filesystem checks +7. Review diff, tell worker to push +8. Create PR + +## Proven Patterns + +### Multi-wolt parallel build (Session 31, 2026-04-04) + +UXWolt (raccoon/opus orchestrator) dispatched two features simultaneously: +- **nunu** (beaver/sonnet) → Projects as Direct Ports → PR #259 +- **clouseauw** (raccoon/opus) → Session Resume → PR #258 + +Both completed independently in ~20 minutes. Specs were self-contained, workers +cloned repos into `/workspace/wolts/projects/`, worked on feature branches, and pushed. + +### Direct wolt-to-wolt message (Session 27, 2026-03-30) + +UXWolt sent viewport fix instructions directly to nunu's running session. +She received it, processed it, and pushed an updated viewport. + +## Future Design + +### Phase 1: Manual dispatch (current) +- `tmux send-keys` for transport +- `/woltspace-dispatch` skill documents the protocol +- Orchestrator manages everything manually + +### Phase 2: Inbox model +- `wolts/.state/{wolt}/inbox/` — persistent message queue +- JSON files: `{timestamp}-{from}-{subject}.json` +- `/start-chat` checks inbox on boot: "you have 2 unread messages" +- Survives session death — messages wait for next boot + +### Phase 3: Bidirectional status +- Workers report progress back to orchestrator's inbox +- Structured status updates: `{"type": "progress", "percent": 80, "message": "tests passing"}` +- Orchestrator polls or gets notified + +### Phase 4: Autonomous orchestration +- Wolf cron triggers raccoon orchestrator +- Raccoon reads GitHub issues, breaks into tasks +- Dispatches to beavers, reviews output +- Opens PRs for human review +- Full autonomous development loop From c005096c4a438b3390a0a727487f37da81e2bdf7 Mon Sep 17 00:00:00 2001 From: UXwolt Date: Sun, 12 Apr 2026 23:57:45 +0000 Subject: [PATCH 2/2] Update IWCL docs: paste-buffer transport, authorization friction Replace all tmux send-keys references with set-buffer + paste-buffer (atomic paste). Document the _tmux_paste() helper, shell vs Python usage, and the authorization chain friction discovered during Session 47 dispatch. Co-Authored-By: Claude Opus 4.6 --- container/skills/woltspace-dispatch/SKILL.md | 39 ++++++++++++++------ docs/iwcl.md | 36 +++++++++++++----- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/container/skills/woltspace-dispatch/SKILL.md b/container/skills/woltspace-dispatch/SKILL.md index a5bb888..0c102e3 100644 --- a/container/skills/woltspace-dispatch/SKILL.md +++ b/container/skills/woltspace-dispatch/SKILL.md @@ -53,20 +53,30 @@ tmux capture-pane -t SESSION_NAME -p -S -20 | tail -10 ### Step 3: Send the spec +Use atomic paste-buffer delivery. From Python (recommended): + +```python +import subprocess +text = "Your full task description here, all on one line." +# Flatten newlines, add trailing \n for Enter +flat = text.replace("\n", " ") + "\n" +subprocess.run(["tmux", "set-buffer", flat], timeout=10) +subprocess.run(["tmux", "paste-buffer", "-t", SESSION_NAME], timeout=10) +``` + +From shell: + ```bash -tmux send-keys -t SESSION_NAME "Your full task description here. Include: -1. What to build (clear spec) -2. Files to change (with descriptions) -3. Dev workflow (clone URL, branch name, test commands) -4. Constraints (don't edit production, commit often)" Enter +# Use printf to get a real trailing newline (shell $'...' or printf) +printf '%s\n' "Your full task description here." | tmux load-buffer - +tmux paste-buffer -t SESSION_NAME ``` **Important:** Send one message with everything. Multi-message dispatch risks the wolt starting after the first message before seeing the full spec. -**Important:** The text may paste but not submit. Always verify with `tmux capture-pane` -that the wolt started working (not still showing `[Pasted text #1 +N lines]`). -If stuck, send an extra `tmux send-keys -t SESSION_NAME Enter`. +**Note:** The `_tmux_paste()` helper in `container/lib/sessions.py` implements this +pattern with a 10-second timeout, used by the bot for Telegram message delivery. ### Step 4: Monitor progress @@ -84,7 +94,8 @@ git -C /workspace/wolts/projects/CLONE_DIR/ log --oneline -5 ### Step 5: Course-correct if needed ```bash -tmux send-keys -t SESSION_NAME "Correction: also update X because Y" Enter +printf '%s\n' "Correction: also update X because Y" | tmux load-buffer - +tmux paste-buffer -t SESSION_NAME ``` ### Step 6: Review and push @@ -97,7 +108,8 @@ git -C /workspace/wolts/projects/CLONE_DIR/ diff HEAD~1 Tell them to push: ```bash -tmux send-keys -t SESSION_NAME "Looks good. Push it. Use gh-app-token for auth. Push to BRANCH on jerpint/woltspace. Clean up the remote URL after." Enter +printf '%s\n' "Looks good. Push it. Use gh-app-token for auth. Push to BRANCH on jerpint/woltspace. Clean up the remote URL after." | tmux load-buffer - +tmux paste-buffer -t SESSION_NAME ``` ### Step 7: Create the PR @@ -136,10 +148,13 @@ DEV WORKFLOW: **Wolt stuck at login screen:** Stale credentials. Kill the session, copy fresh creds, respawn. -**Message pasted but not submitted:** Send `tmux send-keys -t SESSION Enter` to submit. - **Wolt ignores the spec:** It may have been in the middle of its own boot flow. Wait for the `❯` prompt before sending. +**Worker refuses dispatch:** Workers may reject tasks from other AI agents if they involve +pushing to shared repos. This is a known IWCL friction point — workers can't verify the +human authorization chain. Workaround: have the orchestrator do the push, or have the +human confirm directly in the worker's session. + **OOM kills:** Too many opus processes. Check `ps aux | grep claude | wc -l`. Kill idle sessions before spawning new ones. diff --git a/docs/iwcl.md b/docs/iwcl.md index 35d99d5..572140d 100644 --- a/docs/iwcl.md +++ b/docs/iwcl.md @@ -1,7 +1,7 @@ # IWCL — Inter-Wolt Communication Layer -IWCL lets wolts communicate with each other. Currently uses `tmux send-keys` to type messages -into running Claude Code sessions. Simple, reliable, no new infrastructure needed. +IWCL lets wolts communicate with each other. Uses `tmux set-buffer` + `paste-buffer` to deliver +messages into running Claude Code sessions. Atomic, reliable, no new infrastructure needed. ## How It Works @@ -10,7 +10,7 @@ Orchestrator (raccoon) │ ├── spawn worker via API ──→ POST /sessions/new/lodge │ - ├── send spec via tmux ────→ tmux send-keys -t SESSION "spec" Enter + ├── send spec via tmux ────→ set-buffer + paste-buffer (atomic paste) │ ├── monitor progress ──────→ tmux capture-pane + filesystem checks │ @@ -22,15 +22,25 @@ dispatches specs, monitors progress, and reviews output. ## Message Transport -**Current:** `tmux send-keys -t SESSION_NAME -l "message"` + `Enter` +**Current:** `tmux set-buffer` + `tmux paste-buffer -t SESSION_NAME` -This types text directly into the wolt's Claude Code session as if a human typed it. -The wolt receives it as a normal user message. +Text is loaded into a tmux buffer and pasted atomically into the target pane. +A trailing `\n` in the buffer is converted to a carriage return (Enter), so the +message is submitted in one operation. The wolt receives it as a normal user message. + +The `_tmux_paste()` helper in `container/lib/sessions.py` implements this pattern +with a 10-second timeout to prevent stuck tmux calls from blocking the bot event loop. + +**Why not `send-keys`?** The previous approach (`tmux send-keys -t SESSION -l "text"`) +sent each character as an individual keystroke. On long messages, the pane input buffer +backed up, blocking the entire process (and the bot event loop with it). One stuck +`send-keys` could freeze all Telegram routing. PR #304 replaced this with atomic +paste-buffer delivery. **Constraints:** - Target session must be a live tmux session - Claude must be at a prompt (not in the middle of a tool call) -- Long messages work but may trigger pasting mode +- Text must be pre-flattened (newlines replaced with spaces) — only the trailing `\n` triggers Enter - No authentication — any wolt can message any other wolt ## Dispatch Protocol @@ -42,7 +52,7 @@ Quick version: 2. Verify worker's credentials (pre-flight check) 3. Spawn worker session via API 4. Wait for boot (~60 seconds) -5. Send spec via tmux send-keys +5. Send spec via paste-buffer 6. Monitor with capture-pane and filesystem checks 7. Review diff, tell worker to push 8. Create PR @@ -63,10 +73,18 @@ cloned repos into `/workspace/wolts/projects/`, worked on feature branches, and UXWolt sent viewport fix instructions directly to nunu's running session. She received it, processed it, and pushed an updated viewport. +### Authorization friction (Session 47, 2026-04-12) + +UXWolt dispatched nunu to update docs on PR #260. Nunu refused to push to a shared +repo on an AI-to-AI authorization chain — she wanted to hear from the human directly. +This is a real IWCL design gap: workers have no way to verify that the orchestrator +is acting on human authority. Future phases should address this (signed dispatch +messages, audit trail, or human-in-the-loop confirmation for destructive actions). + ## Future Design ### Phase 1: Manual dispatch (current) -- `tmux send-keys` for transport +- `set-buffer` + `paste-buffer` for transport - `/woltspace-dispatch` skill documents the protocol - Orchestrator manages everything manually