Dual OpenClaw + n8n Architecture

Worker → n8n → Supervisor: A setup guide for two Mac Minis with deterministic 30-minute bounded cycles, task-board coordination, and LLM-powered quality review.

┌────────────────────────────────────────────────────────────────────┐ │ THE 30-MINUTE BOUNDED CYCLE │ │ │ │ :00 ─── n8n TRIGGER ────────────────────────────────────────── │ │ │ │ │ ▼ Step 1 │ │ ┌──────────────────┐ │ │ │ Worker (Mini #1) │ "Read the task board. Pick the top │ │ │ Claude / OpenClaw│ TODO. Do it. Move it to DONE." │ │ └────────┬─────────┘ │ │ │ (up to 10 min timeout) │ │ ▼ Step 2 │ │ ┌──────────────────┐ │ │ │ n8n waits │ Reads task board directly. │ │ │ (postal service) │ Sends original prompt + output verbatim. │ │ └────────┬─────────┘ │ │ │ │ │ ▼ Step 3 │ │ ┌──────────────────┐ │ │ │ Supervisor (#2) │ "Review DONE items. Push bad work back. │ │ │ Grok / Gemini │ Reprioritize TODO list." │ │ └────────┬─────────┘ │ │ │ │ │ ▼ Step 4 │ │ ✅ Cycle ends. Everyone sleeps until :30. │ │ │ │ :30 ─── n8n TRIGGER (next cycle begins) ─────────────────────── │ └────────────────────────────────────────────────────────────────────┘ SHARED TASK BOARD (markdown file on synced external drive) ┌─────────────────────────────────────────────────────┐ │ ## TODO ## IN PROGRESS │ │ - Research X - Compile report Y │ │ - Write draft Z │ │ │ │ ## DONE ## BLOCKED │ │ - Task A ✓ - Task C (needs API key) │ │ rating: 4/5 │ └─────────────────────────────────────────────────────┘

Table of Contents

  1. Hardware Setup
  2. Install OpenClaw on Mini #2
  3. Install n8n on Mini #2
  4. The Task Board
  5. The 30-Minute Bounded Cycle
  6. n8n Workflow Implementation
  7. Budget Cap & Cost Control
  8. Daniel's Role
  9. One-Way Database Sync
  10. Monitoring & Alerts
  11. Migration Plan
  12. Honest Limitations

Part 1: Hardware Setup

1.1 Unbox & Headless Setup for Mini #2

No monitor needed. Connect Mini #2 to the same network (Ethernet preferred for rsync reliability).

Enable Remote Login (SSH) on first boot

If you have a monitor temporarily, or use Apple Configurator:

## On Mini #2 (with temporary monitor or via Apple Remote Desktop)
sudo systemsetup -setremotelogin on
sudo dscl . -append /Groups/com.apple.access_ssh GroupMembership $(whoami)

If setting up fully headless, use Apple Configurator 2 from Mini #1 or connect a monitor briefly for initial setup.

1.2 Static IPs / Hostnames

Option A: Reserve IPs in your router's DHCP settings. Option B: Set static IPs on each machine.

## On Mini #1 — set static IP (example: 192.168.1.50)
sudo networksetup -setmanual "Ethernet" 192.168.1.50 255.255.255.0 192.168.1.1

## On Mini #2 — set static IP (example: 192.168.1.51)
sudo networksetup -setmanual "Ethernet" 192.168.1.51 255.255.255.0 192.168.1.1

Add hostnames to /etc/hosts on both machines:

## Add to /etc/hosts on BOTH machines
192.168.1.50    mini1 mini1.local worker
192.168.1.51    mini2 mini2.local supervisor

1.3 SSH Key Exchange

## On Mini #1 (worker) — generate key if needed
ssh-keygen -t ed25519 -C "mini1-worker" -f ~/.ssh/id_ed25519 -N ""

## Copy key to Mini #2
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@mini2

## On Mini #2 (supervisor) — generate key and copy to Mini #1
ssh-keygen -t ed25519 -C "mini2-supervisor" -f ~/.ssh/id_ed25519 -N ""
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@mini1

## Verify both directions work
ssh user@mini2 "echo 'Mini #1 → Mini #2: OK'"
ssh user@mini1 "echo 'Mini #2 → Mini #1: OK'"

1.4 SSH Config (convenience)

## ~/.ssh/config on Mini #1
Host mini2
    HostName 192.168.1.51
    User aroundtheworld
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60

## ~/.ssh/config on Mini #2
Host mini1
    HostName 192.168.1.50
    User aroundtheworld
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60

Part 2: Install OpenClaw on Mini #2

2.1 Install OpenClaw

## On Mini #2
# Install Node.js (if not present)
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
source ~/.zshrc
nvm install 22
nvm use 22

# Install OpenClaw
npm install -g openclaw

# Verify
openclaw --version

2.2 Configure with a Different Model

Key principle: Use a different LLM provider than Mini #1 (Claude). This gives you independent failure modes — if Anthropic is down, your supervisor still runs.
## Initialize OpenClaw on Mini #2
openclaw init

## Edit ~/.config/openclaw/openclaw.json
## Set the model to Grok 4 or Gemini 2.5 Pro

Example config for Grok:

{
  "model": "xai/grok-4",
  "keys": {
    "xai": "xai-YOUR_KEY_HERE"
  },
  "agent": {
    "name": "supervisor",
    "role": "Review worker output for quality, completeness, and accuracy. Update the task board after each review."
  }
}

Example config for Gemini:

{
  "model": "google/gemini-2.5-pro",
  "keys": {
    "google": "YOUR_GOOGLE_AI_KEY"
  },
  "agent": {
    "name": "supervisor",
    "role": "Review worker output for quality, completeness, and accuracy. Update the task board after each review."
  }
}

2.3 Install Matching Skills/Tools

## List skills on Mini #1 first
ssh mini1 "openclaw skills list"

## Install the same skills on Mini #2 (example)
openclaw skills install web-search
openclaw skills install web-fetch
openclaw skills install browser
openclaw skills install memory

## Install plugins
openclaw plugins install openclaw-mem0

2.4 Set Up Supervisor AGENTS.md

cat > ~/clawd/AGENTS.md << 'EOF'
# Supervisor Agent

## Role
You are the SUPERVISOR. You review output from the Worker (Mini #1/Claude) each cycle.
You do NOT generate primary content. You REVIEW it and manage the task board.

## Each Cycle: What You Do
1. Read the task board at ~/clawd/shared/task_board.md
2. Review everything just moved to DONE by the worker
3. For each DONE item:
   - Check factual accuracy — are claims sourced?
   - Check completeness — was the task fully addressed?
   - Check formatting — is it clean, consistent, deliverable?
   - Add a quality rating (1-5) and brief notes
4. If output fails quality gates: move the item BACK to TODO with specific notes
5. Reprioritize the TODO list based on current context
6. Add any follow-on tasks generated by your review

## Task Board Format
Edit ~/clawd/shared/task_board.md directly using the write/edit tools.
Always preserve the four sections: TODO, IN PROGRESS, DONE, BLOCKED.
When moving items, move the ENTIRE entry including context and timestamps.

## What You Don't Do
- Do not generate primary research, write drafts, or run scripts unprompted
- Do not move items from TODO to DONE yourself — that's the worker's job
- Do not hold back feedback to be polite; bad work goes back to TODO
EOF

2.5 Start OpenClaw Gateway on Mini #2

openclaw gateway start

## Verify it's running
openclaw gateway status

Part 3: Install n8n on Mini #2

3.1 Install n8n via npm

## Install globally
npm install -g n8n

## Verify
n8n --version

3.2 Configure n8n Environment

## Create n8n config directory
mkdir -p ~/.n8n

## Set environment variables (add to ~/.zshrc)
cat >> ~/.zshrc << 'EOF'

# n8n Configuration
export N8N_PORT=5678
export N8N_PROTOCOL=http
export N8N_HOST=0.0.0.0
export WEBHOOK_URL=http://mini2:5678/
export N8N_BASIC_AUTH_ACTIVE=true
export N8N_BASIC_AUTH_USER=admin
export N8N_BASIC_AUTH_PASSWORD=CHANGE_THIS_TO_A_STRONG_PASSWORD
export N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
export GENERIC_TIMEZONE=America/New_York
EOF

source ~/.zshrc

3.3 Run n8n as a launchd Service

## Create the launchd plist
cat > ~/Library/LaunchAgents/com.n8n.server.plist << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.n8n.server</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/aroundtheworld/.nvm/versions/node/v22.22.0/bin/n8n</string>
        <string>start</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>N8N_PORT</key>
        <string>5678</string>
        <key>N8N_PROTOCOL</key>
        <string>http</string>
        <key>N8N_HOST</key>
        <string>0.0.0.0</string>
        <key>GENERIC_TIMEZONE</key>
        <string>America/New_York</string>
        <key>N8N_BASIC_AUTH_ACTIVE</key>
        <string>true</string>
        <key>N8N_BASIC_AUTH_USER</key>
        <string>admin</string>
        <key>N8N_BASIC_AUTH_PASSWORD</key>
        <string>CHANGE_THIS</string>
        <key>PATH</key>
        <string>/Users/aroundtheworld/.nvm/versions/node/v22.22.0/bin:/usr/local/bin:/usr/bin:/bin</string>
    </dict>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/aroundtheworld/.n8n/n8n.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/aroundtheworld/.n8n/n8n-error.log</string>
</dict>
</plist>
PLIST

## Load and start
launchctl load ~/Library/LaunchAgents/com.n8n.server.plist

## Verify
curl -s http://localhost:5678/healthz

3.4 Access n8n UI

From any machine on the network:

## Open in browser
open http://mini2:5678

## Or via SSH tunnel from Mini #1
ssh -L 5678:localhost:5678 mini2
## Then open http://localhost:5678

3.5 n8n Management Commands

## Stop n8n
launchctl unload ~/Library/LaunchAgents/com.n8n.server.plist

## Start n8n
launchctl load ~/Library/LaunchAgents/com.n8n.server.plist

## View logs
tail -f ~/.n8n/n8n.log

## View error logs
tail -f ~/.n8n/n8n-error.log

Part 4: The Task Board

The task board is the single source of truth. It's a shared markdown file that both agents read and write — the simplest coordination layer that actually works.

4.1 Location & Sync

Store the task board on a synced external drive (iCloud Drive, Dropbox, or a shared NAS mount) so both Minis can access it directly. The file path must be identical on both machines.

## Example: iCloud Drive (same path on both Minis)
~/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md

## Example: Network share mounted on both
/Volumes/SharedDrive/clawd/task_board.md

## Or: rsync the board as part of the existing sync script
## (see Part 9 — One-Way Sync, two-way for task board only)
⚠️ Note on two-way sync: The task board is the one exception to the "Mini #2 never writes back" rule. The supervisor updates it, so you need a two-way sync for this single file, or use a cloud-synced folder that both Minis see natively.

4.2 Task Board Format

## ~/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md

# Task Board
_Last updated: 2026-02-17 07:15 ET_

---

## TODO

- **Research competitors for QMD expansion**
  - Context: Focus on Veeva and IQVIA. Pull recent earnings mentions of AI/data strategy.
  - Created by: Daniel
  - Added: 2026-02-17 06:00 ET
  - Priority: HIGH

- **Write draft exec summary for pharma deal pitch**
  - Context: Combine last 3 pharma intel reports. Target: CFO-level, 1 page.
  - Created by: supervisor (follow-on from "pharma scan 2026-02-16")
  - Added: 2026-02-17 07:15 ET
  - Priority: MEDIUM

---

## IN PROGRESS

- **Scan FDA filings for rare disease mentions (Q1 2026)**
  - Context: Started this cycle. Scanning EDGAR full-text.
  - Started: 2026-02-17 07:01 ET

---

## DONE

- **Pull Copper pipeline data and flag stale deals**
  - Context: Any deal untouched >14 days gets flagged.
  - Created by: Daniel
  - Completed: 2026-02-17 06:32 ET
  - Output: 23 deals reviewed, 4 flagged. Full report: ~/clawd/output/copper_stale_2026-02-17.md
  - Supervisor rating: 4/5
  - Supervisor notes: Good coverage. Next time include deal owner name alongside stage.

- **Summarize Slack #war-room from past 48h**
  - Created by: supervisor (follow-on from weekly summary)
  - Completed: 2026-02-16 18:10 ET
  - Output: Summary in ~/clawd/output/slack_summary_2026-02-16.md
  - Supervisor rating: 3/5
  - Supervisor notes: Missed the thread about QMD pricing. Moved related follow-up to TODO.

---

## BLOCKED

- **Update embedding pipeline for new schema**
  - Context: Waiting on Daniel to confirm new field names.
  - Blocked by: Daniel — schema decision pending
  - Added: 2026-02-15 14:00 ET

4.3 Task Entry Rules

4.4 Initialize the Board

## Create the shared directory and seed the board
BOARD_PATH="$HOME/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md"
mkdir -p "$(dirname "$BOARD_PATH")"

cat > "$BOARD_PATH" << 'EOF'
# Task Board
_Last updated: (auto-updated each cycle)_

---

## TODO

- **[Add your first task here]**
  - Context: Describe what to do and why.
  - Created by: Daniel
  - Added: YYYY-MM-DD HH:MM ET
  - Priority: HIGH

---

## IN PROGRESS

_(empty — worker will populate during cycles)_

---

## DONE

_(empty — will populate as work completes)_

---

## BLOCKED

_(empty — add items here with blocker details)_
EOF

echo "Task board initialized at: $BOARD_PATH"

Part 5: The 30-Minute Bounded Cycle

Core design principle: Every cycle is bounded. The LLMs are not running continuously — they fire, do one unit of work, and stop. n8n controls the clock. This is the difference between a scheduled assistant and an uncontrolled loop.

5.1 The Four Steps

1
n8n pings the Worker (Mini #1)

Prompt: "Read the task board at [path]. Pick the top TODO item. Do it. Move it from TODO to DONE with a summary of what you did and where the output is. If your work generated new tasks, add them to TODO."

n8n sets a 10-minute maximum timeout. If the worker hasn't responded, n8n moves on and logs a timeout.

2
n8n waits and reads the task board

n8n doesn't trust the worker's response — it reads the task board directly to see what actually moved to DONE. It collects the original prompt it sent plus the worker's response verbatim.

3
n8n pings the Supervisor (Mini #2)

Prompt: "Read the task board at [path]. Review everything just moved to DONE. If the work is bad, move it back to TODO with specific notes on what's wrong. Reprioritize the TODO list based on current priorities."

n8n also passes the original worker prompt and the worker's raw response — verbatim. The worker never controls what the supervisor sees.

4
Cycle ends — everyone sleeps

Both LLMs are idle. No API calls, no spend. n8n idles until the next 30-minute trigger. The task board reflects the current state of all work.

5.2 n8n as the Postal Service

Key principle: n8n is not smart — it's the mail carrier. It reads the task board directly (not through the worker), and it forwards the worker's exact output to the supervisor without filtering or summarizing. The worker cannot manipulate what the supervisor reviews. The supervisor sees the original prompt, the raw response, and the task board state.

This matters because an LLM could, intentionally or not, summarize its own work in a flattering way. By having n8n read the board independently and pass the raw exchange, the supervisor gets an honest picture.

5.3 Why 30 Minutes?

You can change this. 1 hour is fine for lighter workloads. 15 minutes is feasible for urgent tasks but watches the budget (see Part 7).

5.4 What Happens at Each Cycle Slot

TimeWorker doesSupervisor does
:00 – :10Picks top TODO, executes task, updates boardIdle (sleeping)
:10 – :15Idle (done)Reviews DONE items, reprioritizes, updates board
:15 – :30IdleIdle
:30Next cycle beginsNext cycle begins

Part 6: n8n Workflow Implementation

6.1 The Core Cycle Workflow (n8n)

One n8n workflow handles the full cycle. Import this JSON into n8n via Workflows → Import:

## The main cycle workflow runs every 30 minutes.
## Set the cron expression: */30 * * * *

## Workflow structure (pseudocode — adapt node types to your n8n version):

1. Schedule Trigger (every 30 min)
   └─▶ 2. Check Budget (Read daily spend log)
         ├─ [over budget] ─▶ Slack: "🛑 Daily budget exceeded. Cycle skipped."
         └─ [under budget] ─▶ 3. Read Task Board
                                   └─▶ 4. Check: Any TODO items?
                                         ├─ [no tasks] ─▶ Slack: "📋 Task board empty. Add tasks to continue."
                                         └─ [has tasks] ─▶ 5. Ping Worker (OpenClaw #1)
                                                               └─▶ 6. Wait / Timeout (10 min max)
                                                                     ├─ [timeout] ─▶ Log timeout, skip supervisor
                                                                     └─ [response] ─▶ 7. Read Task Board (again, direct)
                                                                                         └─▶ 8. Ping Supervisor (OpenClaw #2)
                                                                                               └─▶ 9. Log cycle + update spend

6.2 Step 5: Ping the Worker

## n8n HTTP Request Node — POST to OpenClaw #1
## URL: http://mini1:3000/api/v1/message
## Timeout: 600000ms (10 minutes)

{
  "message": "Read the task board at ~/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md\n\nPick the top-priority TODO item. Do it. Then:\n1. Move the item from TODO to IN PROGRESS at the start (mark started timestamp)\n2. Complete the work\n3. Move it to DONE with:\n   - Summary of what you did\n   - Where the output is (file path, URL, etc.)\n   - Timestamp completed\n4. If your work generated follow-on tasks, add them to TODO with context.\n\nDo exactly one task. Do not pick up additional items.",
  "channel": "api"
}

6.3 Step 7: Read Task Board Directly

## n8n Execute Command node — n8n reads the board itself
## This is where n8n acts as the postal service: it doesn't trust the worker's claim
## It reads what actually changed on the board

BOARD="$HOME/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md"
cat "$BOARD"

## Also capture the worker response from Step 6 for passing to supervisor verbatim:
## {{$node["Ping Worker"].json.response}}

6.4 Step 8: Ping the Supervisor

## n8n HTTP Request Node — POST to OpenClaw #2
## URL: http://localhost:3000/api/v1/message  (supervisor is on the same machine as n8n)
## Timeout: 300000ms (5 minutes)

## The key: pass BOTH the task board state AND the raw worker exchange verbatim

{
  "message": "SUPERVISOR REVIEW — CYCLE {{$now.format('YYYY-MM-DD HH:mm')}}\n\n## Task Board (current state)\n{{$node['Read Task Board'].json.stdout}}\n\n## Worker exchange (verbatim)\nOriginal prompt sent to worker:\n{{$node['Ping Worker'].json.prompt}}\n\nWorker response:\n{{$node['Ping Worker'].json.response}}\n\n---\nYour job:\n1. Review everything in the DONE section that has no supervisor rating yet\n2. Add your rating (1-5) and notes to each DONE entry\n3. If work is poor or incomplete, move the item back to TODO with specific notes on what's wrong\n4. Reprioritize the TODO list. Put the highest-value tasks first.\n5. Add any follow-on tasks from your review to TODO.",
  "channel": "api"
}
⚠️ Why verbatim matters: The supervisor receives the original prompt n8n sent AND the worker's full response. The worker does not summarize or filter this for the supervisor. If the worker did sloppy work and then wrote a polished summary in the task board, the supervisor can catch the discrepancy.

6.5 Step 9: Log the Cycle

## n8n Execute Command node — append to cycle log
cat >> ~/.n8n/cycle_log.jsonl << EOF
{"timestamp":"$(date -Iseconds)","cycle_num":$(wc -l < ~/.n8n/cycle_log.jsonl 2>/dev/null || echo 0),"worker_status":"{{$node['Ping Worker'].json.status}}","supervisor_status":"{{$node['Ping Supervisor'].json.status}}","estimated_cost_usd":{{$json.estimated_cost}}}
EOF

6.6 Timeout Handling

## n8n IF node after worker step:
## If worker response time > 600 seconds OR response contains error:

## On timeout: log it and skip the supervisor for this cycle
cat >> ~/.n8n/cycle_log.jsonl << EOF
{"timestamp":"$(date -Iseconds)","event":"worker_timeout","cycle_skipped_supervisor":true}
EOF

## Alert Daniel if timeouts are accumulating (see Monitoring — Part 10)
## Don't alert on every timeout — alert if 3+ in a row

6.7 Worker AGENTS.md (Mini #1)

cat > ~/clawd/AGENTS.md << 'EOF'
# Worker Agent

## Role
You execute tasks from the task board. One task per cycle, no more.
You are triggered by n8n every 30 minutes when there is work to do.

## Each Cycle: What You Do
1. Read the task board at the shared path
2. Pick the HIGHEST priority TODO item
3. Move it to IN PROGRESS (add started timestamp)
4. Do the work — use your full tool set
5. Move it to DONE with: output location, summary, timestamp
6. If work generated follow-on tasks, add to TODO with context and "Created by: worker"
7. Stop. Do not pick up another task.

## Rules
- One task per trigger. Not two. Not "while I'm at it..."
- If a task is too large for one cycle, break it into subtasks in TODO and do the first one
- Mark BLOCKED if you cannot proceed without external input (state the blocker clearly)
- Do NOT update the DONE entry with a vague summary. Be specific: what was produced, where is it.
EOF

Part 7: Budget Cap & Cost Control

⚠️ LLMs in loops will spend infinite money. An unbounded 30-minute cycle that fires 48 times a day, each triggering two LLM calls, can easily run up $100–$500/day depending on context length and model. Set a hard daily cap before you activate anything.

7.1 Daily Spend Tracking

## Create a daily spend log on Mini #2
## Each cycle, n8n estimates the cost and appends to a daily file

## Estimate: read token counts from API responses
## Claude Sonnet ≈ $3/M input tokens, $15/M output tokens
## Grok-4 / Gemini: check current pricing

## Daily spend log location
SPEND_LOG="$HOME/.n8n/spend_$(date +%Y-%m-%d).json"

## Initialize if new day
if [ ! -f "$SPEND_LOG" ]; then
  echo '{"date":"'$(date +%Y-%m-%d)'","total_usd":0,"cycles":0,"daily_limit_usd":50}' > "$SPEND_LOG"
fi

cat "$SPEND_LOG"

7.2 Kill Switch: Check Before Every Cycle

## n8n Function node — runs FIRST in every cycle workflow
## If over budget, abort the cycle

const spendFile = `${process.env.HOME}/.n8n/spend_${new Date().toISOString().slice(0,10)}.json`;
let spend;
try {
  spend = JSON.parse(require('fs').readFileSync(spendFile, 'utf8'));
} catch(e) {
  // No spend file yet — new day, reset
  spend = { date: new Date().toISOString().slice(0,10), total_usd: 0, cycles: 0, daily_limit_usd: 50 };
}

if (spend.total_usd >= spend.daily_limit_usd) {
  return [{
    json: {
      abort: true,
      reason: `Daily budget $${spend.daily_limit_usd} exceeded. Spent: $${spend.total_usd.toFixed(2)}. Cycles run: ${spend.cycles}.`
    }
  }];
}

return [{ json: { abort: false, current_spend: spend.total_usd, limit: spend.daily_limit_usd } }];

7.3 Update Spend After Each Cycle

## n8n Execute Command node — appended at end of each cycle
## Rough estimate based on message lengths (improve with actual token counts from API)

SPEND_LOG="$HOME/.n8n/spend_$(date +%Y-%m-%d).json"
ESTIMATED_COST={{$json.estimated_cycle_cost_usd}}   ## pass from Function node

jq --argjson cost "$ESTIMATED_COST" '
  .total_usd += $cost |
  .cycles += 1
' "$SPEND_LOG" > /tmp/spend_tmp.json && mv /tmp/spend_tmp.json "$SPEND_LOG"

7.4 Alert When Approaching the Limit

## Alert at 80% of daily limit
## In the budget check Function node, add:

if (spend.total_usd >= spend.daily_limit_usd * 0.8 && !spend.alerted_80pct) {
  // Trigger Slack alert
  spend.alerted_80pct = true;
  return [{
    json: {
      abort: false,
      alert: true,
      alert_message: `⚠️ API spend at 80% of daily limit. $${spend.total_usd.toFixed(2)} / $${spend.daily_limit_usd}. ${spend.cycles} cycles run today.`
    }
  }];
}

7.5 Configuring the Daily Limit

## To change the daily limit, update the spend file for today
## OR set it in n8n environment variables

## In ~/.zshrc:
export N8N_DAILY_BUDGET_USD=50   ## default $50

## More conservative for testing:
export N8N_DAILY_BUDGET_USD=10

## To pause everything immediately:
## In n8n UI → disable the cycle workflow
## Or: set the limit to 0 in the current day's spend file
SPEND_LOG="$HOME/.n8n/spend_$(date +%Y-%m-%d).json"
jq '.daily_limit_usd = 0' "$SPEND_LOG" > /tmp/spend_tmp.json && mv /tmp/spend_tmp.json "$SPEND_LOG"

Part 8: Daniel's Role

You are not out of the loop — you're just not in the critical path. The system runs cycles without you, but you define what those cycles are working toward.

8.1 Seeding the Board

The task board starts empty. You seed it with strategic goals. The worker and supervisor generate follow-on tasks, but they're all downstream of what you put at the top.

## Add a new goal to the task board
## Open the file directly and add a TODO entry:
open "~/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md"

## Or append via command line:
cat >> "~/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md" << 'EOF'

- **[Your goal here]**
  - Context: [Everything an LLM needs to act on this cold — no assumed state]
  - Created by: Daniel
  - Added: 2026-02-17 09:00 ET
  - Priority: HIGH
EOF

8.2 How Work Flows

WhoAction
DanielAdds high-level goals to TODO ("Research X", "Write draft Y for Z audience")
WorkerPicks top TODO, may break large goals into smaller subtasks, executes, moves to DONE
SupervisorReviews DONE, rates quality, rejects bad work back to TODO, adds follow-on tasks from findings
DanielChecks DONE section when he wants to see results, adds new goals as priorities shift

8.3 Reviewing the DONE Section

You don't need to review every cycle. Check the DONE section when:

The supervisor's quality ratings give you a quick signal. A pattern of 2/5s means either the tasks are poorly specified or the worker is struggling with that class of work.

8.4 Changing Priorities

## To reprioritize immediately, edit the TODO section directly
## Move the most urgent item to the top. The worker picks from the top.

## To stop a specific line of work, move it to BLOCKED:
## BLOCKED: "Research X — deprioritized, revisit next month"

## To abandon a class of tasks, archive the DONE section periodically:
DONE_ARCHIVE="$HOME/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/done_archive_$(date +%Y-%m).md"
# Cut the DONE section from task_board.md and append to archive file

8.5 What You Don't Need to Do

Part 9: One-Way Database Sync

The task board is synced via cloud (iCloud/Dropbox). Everything else — the QMD database, workspace files — syncs one-way from Mini #1 to Mini #2 so the supervisor has read access to source data.

9.1 Sync Script (Mini #2 pulls from Mini #1)

## Create sync script on Mini #2
cat > ~/scripts/sync_from_worker.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

LOG_FILE="$HOME/.n8n/sync.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$TIMESTAMP] Starting sync from Mini #1..." >> "$LOG_FILE"

# Sync QMD database (read-only mirror for supervisor context)
rsync -avz --delete \
  --exclude='*.lock' \
  --exclude='*.tmp' \
  mini1:~/clawd/qmd/ \
  ~/clawd/qmd-mirror/ \
  >> "$LOG_FILE" 2>&1

# Sync key workspace files
rsync -avz --delete \
  --exclude='node_modules/' \
  --exclude='.git/' \
  --exclude='*.lock' \
  --include='config/***' \
  --include='memory/***' \
  --include='output/***' \
  --include='AGENTS.md' \
  --exclude='*' \
  mini1:~/clawd/ \
  ~/clawd/worker-mirror/ \
  >> "$LOG_FILE" 2>&1

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] Sync complete." >> "$LOG_FILE"
SCRIPT

chmod +x ~/scripts/sync_from_worker.sh

9.2 Cron Job (every 30 minutes, offset from cycle)

## Run at :15 and :45 — between cycles, not during them
crontab -e

## Add:
15,45 * * * * /Users/aroundtheworld/scripts/sync_from_worker.sh

9.3 Verify Sync Is One-Way

## Run manually first
~/scripts/sync_from_worker.sh

## Verify the mirror exists
ls -la ~/clawd/qmd-mirror/
ls -la ~/clawd/worker-mirror/

## IMPORTANT: There is no reverse rsync.
## Mini #2 PULLS from Mini #1. Never pushes.
## Task board sync is handled by iCloud/Dropbox — not rsync.
⚠️ Important: Mini #2 never writes back to Mini #1 via rsync. The only shared write surface is the task board via cloud sync. If you need to pass data from supervisor to worker beyond the task board, use Slack messages or a shared API endpoint — never rsync in reverse.

Part 10: Monitoring & Alerts

10.1 n8n Built-in Dashboard

n8n's UI at http://mini2:5678 shows all workflow executions with pass/fail, timing, and error details. For the cycle workflow, you'll see every 30-minute run logged here — a simple audit trail of what ran and what failed.

10.2 Slack Alerts — What Triggers a Message

EventAlert
Daily budget 80% reachedWarning DM
Daily budget exceededAlert + cycle halted
Worker timeout (10 min) — 3 in a rowAlert DM
Task board unreadable / missingAlert DM
Mini #1 unreachable for 3+ pingsAlert DM
Supervisor failed to respondLogged only (cycle still complete)
TODO section emptyGentle nudge (optional)
## Slack alert node configuration (reuse across workflows)
## n8n Slack credentials: add via n8n UI → Credentials → Slack API
## Channel: D0AD0P29PRP (Daniel's DM)
{
  "channel": "D0AD0P29PRP",
  "text": "🚨 *{{$workflow.name}}*: {{$json.alert_message}}\nTime: {{$now.format('h:mm a')}} ET"
}

10.3 Daily Cycle Summary (8pm ET — no LLM)

## Separate n8n workflow: runs at 8pm ET, purely deterministic

## Execute Command node:
SPEND_LOG="$HOME/.n8n/spend_$(date +%Y-%m-%d).json"
CYCLE_LOG="$HOME/.n8n/cycle_log.jsonl"
BOARD="$HOME/Library/Mobile Documents/com~apple~CloudDocs/clawd-shared/task_board.md"

echo "SPEND:$(cat $SPEND_LOG)"
echo "CYCLES:$(wc -l < $CYCLE_LOG)"
echo "DONE_COUNT:$(grep -c '^- \*\*' "$BOARD" | head -1)"

## Function node: build Slack summary from the above data
## No LLM involved — just string formatting in n8n's JS engine

const spend = JSON.parse($input.first().json.SPEND);
const cycles = parseInt($input.first().json.CYCLES);

return [{
  json: {
    summary: [
      `📊 *Daily Cycle Summary — ${new Date().toLocaleDateString('en-US', {timeZone:'America/New_York', weekday:'long', month:'short', day:'numeric'})}*`,
      ``,
      `🔄 Cycles run: ${cycles} / 48 possible`,
      `💰 API spend: $${spend.total_usd.toFixed(2)} / $${spend.daily_limit_usd}`,
      `✅ Tasks completed: (check task board DONE section)`,
      ``,
      `_Review task board to see what was accomplished_`
    ].join('\n')
  }
}];

10.4 System Health Monitor (every 5 min)

## Lightweight workflow — pings Mini #1 and checks n8n itself
## Cron: */5 * * * *

## Execute Command:
ssh -o ConnectTimeout=5 mini1 'echo OK' 2>/dev/null || echo 'UNREACHABLE'

## Track consecutive failures in a counter file
FAIL_FILE="$HOME/.n8n/mini1_failures"
if [ "$(cat /tmp/mini1_check)" = "UNREACHABLE" ]; then
  echo $(($(cat $FAIL_FILE 2>/dev/null || echo 0) + 1)) > $FAIL_FILE
else
  echo 0 > $FAIL_FILE
fi

FAILS=$(cat $FAIL_FILE)
if [ "$FAILS" -ge 3 ]; then
  echo "ALERT: Mini #1 unreachable for $FAILS consecutive checks"
fi

Part 11: Migration Plan

11.1 Start Small — Validate the Cycle First

Before migrating any existing pipelines, run 10–20 cycles on a low-stakes task. Observe the full loop: task board update, supervisor review, quality ratings. Make sure the cycle is actually working before adding complexity.

## Suggested first task for testing the cycle:
## "Research the top 3 n8n competitors. Write a 2-paragraph summary
##  comparing their pricing models. Save to ~/clawd/output/n8n_competitors.md"
##
## Why: short, verifiable, supervisor can easily evaluate quality

11.2 Migration Tiers

TierTasksWhenRisk
Tier 1On-demand research tasks from the task boardWeek 1 — nowLow — bounded by definition
Tier 1Delta syncs (email, calendar, Slack digests)Week 2Low — simple check patterns
Tier 2Daily briefing, dashboard refresh, pipeline checksWeek 3-4Medium — need output verification
Tier 3Multi-step research pipelines, weekly summariesWeek 5+Higher — large context, multiple files

11.3 Parallel Running Protocol

## Don't disable existing crons immediately.
## Run n8n cycle alongside existing processes for a week.
## Compare outputs.

## Disable cron on Mini #1 (comment out, don't delete):
ssh mini1 "crontab -l | sed 's/^\(.*morning_briefing.*\)$/# MIGRATED TO N8N CYCLE: \1/' | crontab -"

## Re-enable instantly if needed:
ssh mini1 "crontab -l | sed 's/^# MIGRATED TO N8N CYCLE: //' | crontab -"

11.4 Verification Checklist (per migrated task)

Part 12: Honest Limitations

⚠️ Read This Before You Go Build Mode

This architecture has real value, but it's worth being clear-eyed about what it is and isn't.

12.1 What's Solid

The n8n trigger layer is genuinely valuable regardless of everything else. Replacing ad-hoc crons and manual runs with a visual, observable, Slack-alerting orchestrator is an improvement on its own. You get:

These benefits exist even if you never turn on the supervisor loop.

12.2 The Supervisor Loop Is an Experiment

The bounded cycle with supervisor review is a meaningful improvement over "LLM runs forever." But:

12.3 Setup Requires Real Effort

This is not a one-afternoon project. Realistic time estimates:

PhaseEffort
Hardware + SSH setup2–4 hours (more if headless)
n8n install + config1–2 hours
OpenClaw on Mini #21–2 hours
Task board + first cycle working2–4 hours
Budget cap + monitoring2–3 hours
Migrating first real task1–2 hours
Total to first real cycle~10–17 hours

12.4 This Is an Improvement, Not a Revolution

You won't wake up to a finished product. You'll wake up to a DONE section with 2–4 completed tasks, some of them mediocre, some of them genuinely useful. That's the realistic outcome. The value compounds over days and weeks — not overnight.

The honest summary: