🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Shark

by @keugenek

Enables non-blocking AI agent execution by spawning parallel remora subagents for slow tasks, keeping the main agent responsive and efficient.

Versionv0.1.0
Downloads371
TERMINAL
clawhub install shark

πŸ“– About This Skill


name: shark slug: shark version: 0.3.1 summary: "The Shark Pattern β€” universal non-blocking execution for any AI coding agent. Spawn remoras for slow tools, keep the main agent swimming. Works with Claude Code, Codex, Gemini CLI, Cursor, Aider, OpenClaw." tags: [async, performance, subagents, non-blocking, concurrency, patterns, claude-code, codex, gemini, cursor, aider] homepage: https://github.com/keugenek/shark-pattern author: keugenek

🦈 The Shark Pattern

> *A shark that stops swimming dies. An agent that waits for tools wastes compute.*

Works with: Claude Code Β· Codex Β· Gemini CLI Β· Cursor Β· Windsurf Β· Aider Β· OpenClaw Β· any LLM agent

When to Use This Skill

Trigger this skill when the user says:

  • "use the shark pattern"
  • "non-blocking agent"
  • "never wait for tools"
  • "spawn background workers"
  • "parallel subagents"
  • "keep the main agent moving"
  • or when you notice you're about to block on a slow tool (web fetch, SSH, build, test run, API call)
  • The Rule

    Every LLM turn must complete in under 30 seconds.

    If any operation would take longer: 1. Spawn a remora (sessions_spawn with mode: "run") 2. Continue reasoning immediately 3. Incorporate remora results when they arrive

    You are never in I/O wait. You are always reasoning about something.

    Lifecycle

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  DECOMPOSE  β”‚  Break task into N independent subtasks
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚ spawn N remoras (+ 1 pilot fish when first completes early)
           β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚    SPAWN    β”‚  sessions_spawn Γ— N, all parallel, record session IDs
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚ main agent keeps reasoning (never waits)
           β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     timeout/crash
    β”‚   MONITOR   β”‚ ──────────────────► MARK ⏱/❌ (partial still useful)
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚ all done OR deadline hit
           β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  AGGREGATE  β”‚  Collect results, note failures, merge pilot fish draft
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   REPORT    β”‚  Single coherent response with failure count noted
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    

    No nested remoras. If a remora is running, it executes inline β€” remoras cannot spawn their own remoras. Only the main shark spawns.

    The Pattern

    Bad (Ralph-style blocking):

    think β†’ call slow tool β†’ WAIT 60s β†’ think β†’ call slow tool β†’ WAIT 45s β†’ ...
    

    Good (Shark-style non-blocking):

    think β†’ spawn remora(slow tool) β†’ think about something else
         β†’ spawn remora(another tool) β†’ synthesize partial results
         β†’ receive remora result β†’ incorporate β†’ swim on
    

    Implementation

    When applying the Shark Pattern, structure your work like this:

    1. Identify blocking operations

    Before calling any tool, ask: "Will this take more than 20-30 seconds?"

    Slow tools (always spawn):

  • Web searches / page fetches
  • SSH commands on remote machines
  • Build / test / CI runs
  • File system scans over large directories
  • API calls with unknown latency
  • LLM inference calls (coding agents)
  • Fast tools (run inline, never spawn):

  • Reading local files
  • Simple calculations
  • String manipulation
  • Memory lookups
  • 2. Spawn remoras

    sessions_spawn({
      task: "Do the slow thing and return the result",
      mode: "run",
      runtime: "subagent",
      streamTo: "parent"  // optional: stream output back
    })
    

    Spawn multiple remoras in parallel when possible β€” don't serialize unless there's a data dependency.

    3. Keep the main fin moving

    After spawning, immediately continue:

  • Plan the next step
  • Work on a different part of the task
  • Summarize what you know so far
  • Prepare to incorporate results
  • 4. Incorporate results

    When remora results arrive, weave them in and continue. Never re-do work a remora already completed.

    If your runtime keeps subagents alive after completion, close them once you've incorporated their result. In Codex that means: wait for the remora, use its output, then close_agent(id) unless you intentionally plan to reuse that same agent.

    Timing Budget

    | Operation | Budget | Action | |-----------|--------|--------| | File read | < 2s | Inline | | Web search | 5-30s | Spawn | | SSH command | 10-120s | Spawn | | Build/test | 30-300s | Spawn | | Coding agent | 60-600s | Spawn | | Memory search | < 3s | Inline |

    Example: Multi-Step Research Task

    Without Shark (blocking):

    1. Search web for X        [wait 15s]
    2. Search web for Y        [wait 12s]  
    3. Fetch page Z            [wait 8s]
    4. SSH check server        [wait 30s]
    Total: ~65 seconds blocked
    

    With Shark (non-blocking):

    1. Spawn: search X         [0s - spawned]
    2. Spawn: search Y         [0s - spawned]
    3. Spawn: fetch Z          [0s - spawned]
    4. Spawn: SSH check        [0s - spawned]
    5. Plan synthesis while waiting [15s of actual thinking]
    6. All results arrive β†’ synthesize
    Total: ~15s of thinking + max(tool times) in parallel
    

    Output Format

    Announce on start

    > 🦈 Shark mode β€” spawning [N] remoras for [tasks], continuing...

    Progress bar (chat-friendly, Unicode only β€” no images needed)

    Use this format after each remora or pilot fish completes. Works in Telegram, Discord, Signal, iMessage β€” anywhere.

    🦈 3 remoras · 1 pilot fish

    β—‰ [A] task name here β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ βœ… 9s β—‰ [B] task name here β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ βœ… 33s β—‹ [C] task name here β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ pending β—ˆ [P] Pilot fish β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘ ~14s left

    ↳ continuing...

    Symbols:

  • β—‰ = remora (completed)
  • β—‹ = remora (pending)
  • βŠ™ = remora (running)
  • β—ˆ = pilot fish (time-bounded)
  • β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ = done bar (12 blocks)
  • β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘ = partial (filled = elapsed / total budget)
  • β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ = not started
  • Progress fill: filled = round(elapsed / timeout * 12) blocks of β–ˆ, remainder β–‘

    Only post an update when something changes (remora completes or pilot fish starts/ends). Don't spam β€” one update per event.

    Final synthesis

    After all remoras done: > 🦈 All fins in β€” synthesising [N] results + pilot draft

    Then deliver the report.

    The Pilot Fish Sub-Pattern

    > *Pilot fish swim alongside sharks doing prep work. When you have idle time, use it.*

    When one remora returns early and others are still running:

    1. Spawn a pilot fish β€” a time-bounded analysis sub-agent 2. Give it only the partial results so far + a hard timeout equal to the estimated remaining wait 3. Let it pre-validate, pre-analyse, find patterns, draft conclusions 4. Kill it (or it self-terminates) when the last primary remora completes 5. Incorporate whatever the pilot fish produced into the final synthesis

    remora A ──────► result (early)
    remora B ────────────────────────────► result
    remora C ──────────────────────────────────► result

    main: spawn A, B, C A done β†’ spawn pilot-fish(A's result, timeout=est_remaining) pilot-fish: pre-analyse A, draft partial report, validate data... B done β†’ pilot-fish still running, feed B's result in (or kill+reuse) C done β†’ kill pilot-fish, synthesise A+B+C+pilot-fish draft

    Pilot Fish Rules

  • Always time-bounded β€” pass runTimeoutSeconds equal to estimated remaining wait
  • Never blocks β€” spawned async, main agent continues
  • Opportunistic β€” if it finishes early, bonus; if killed mid-run, partial output is still useful
  • One at a time β€” don't stack pilot fish on pilot fish
  • Task: pre-validate data, find gaps, draft structure, flag anomalies, prepare questions
  • Example

    // remoras A (fast) and B (slow) both spawned
    // A finishes in 10s, B will take another 30s

    // Spawn pilot fish with 25s budget: sessions_spawn({ task: "Pre-analyse these results from remora A. Validate the data, note any gaps, draft the structure of the final report. Stop after 25 seconds.", runTimeoutSeconds: 25, mode: "run" })

    // Main agent continues doing other work // When B finishes β†’ kill pilot fish β†’ synthesise A + B + pilot draft

    Decision Tree β€” When to Spawn

    Before every tool call, ask: "Will this take more than 10 seconds?"

    Estimated time < 10s?  β†’ run inline
    Estimated time β‰₯ 10s?  β†’ spawn remora
    Unknown latency?        β†’ spawn remora (assume slow)
    Data dependency on another remora? β†’ wait, then inline
    Already at 8 remoras? β†’ queue, don't stack
    

    Always spawn: web search/fetch, SSH, build/test, coding agents, CI triggers, API calls with unknown latency Always inline: file read, memory lookup, string ops, math, local config reads


    Error Handling

    remoras will fail, timeout, or return garbage. Plan for it.

    remora timeout

    β—‰ [A] task    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ ⏱ 30s [timeout]
    
  • Treat as partial result β€” use whatever was returned
  • Do not re-spawn the same task (wastes time, likely to timeout again)
  • Note the gap in synthesis: "A timed out β€” data may be incomplete"
  • If A's result is critical, spawn a smaller-scoped follow-up shark
  • remora crash / error

    β—‰ [A] task    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ ❌ [error: connection refused]
    
  • Log the error inline in the progress bar
  • Continue synthesis without that result
  • Mention the failure in the final report
  • Optionally file an issue / alert if it's infrastructure
  • If the runtime still shows the remora as open after completion or error, clean it up immediately. In Codex, close completed remoras with close_agent(id) once their output is delivered.
  • Partial results (most common)

  • Most useful β€” a remora that timed out at 28s has 28s of work in it
  • Always check if partial output is usable before discarding
  • Progress bar: ⏱ = timeout with partial, ❌ = hard error with nothing
  • >50% remoras failed

  • Degrade gracefully β€” fall back to sequential for remaining work
  • Note in report: "⚠️ degraded mode β€” N/M remoras failed"
  • All remoras failed

  • Fall back to sequential execution for the most critical task only
  • Do not spawn another full fleet β€” you're likely hitting a systemic issue
  • Forgetting to spawn the pilot fish (most common mistake)

  • You finished a fast inline task, a remora is still running, and you just... wait
  • Symptom: main agent idle, no pilot fish, time wasted
  • Fix: always ask after any remora completes early β€” "what can I pre-draft right now?"
  • Even if you have nothing obvious, draft the output structure, prepare questions, or outline the synthesis
  • Pilot fish killed mid-run

  • Normal and expected β€” whatever it produced is still useful
  • Incorporate partial pilot fish output into synthesis
  • Don't wait for it or re-spawn it

  • Terminology

  • remora = a sessions_spawn call with mode: "run", runtime: "subagent", and runTimeoutSeconds set. A remora is specifically a *timed* sub-agent β€” untimed subagents are not remoras.
  • Pilot fish = a remora spawned *after* another remora completes, with a short timeout sized to the estimated remaining wait. Purpose: pre-analysis only, never primary work.
  • Fleet = the full set of remoras spawned for one task
  • Fin moving = the main agent is doing useful work (not waiting)
  • No nested remoras = remoras always execute inline β€” only the main shark spawns
  • runTimeoutSeconds β€” confirmed real

    Verified against OpenClaw source: runTimeoutSeconds: z.number().int().min(0).optional() β€” maps to the subagent wait timeout. Use it. Hard-kills the sub-agent process after N seconds, partial output returned.


    Pilot Fish Sizing Formula

    pilotFishTimeout = min(estimatedRemaining * 0.8, 25)
    

  • estimatedRemaining = how long you think the slowest remaining remora will take
  • Cap at 25s so pilot fish always finishes before the main synthesis turn
  • If you don't know: use 20s as default
  • Example: slowest remaining remora estimated at 30s β†’ pilot fish timeout = min(24, 25) = 24s


    Hard Limits

  • Never use yieldMs > 30000 in exec calls β€” this holds the main turn hostage
  • Never process(action=poll, timeout > 20000) in the main session β€” same reason
  • Never add sleep or wait loops in the main thread
  • Always set runTimeoutSeconds on remoras β€” unbound sub-agents are not sharks
  • Always clean up completed remoras β€” if your runtime requires explicit teardown, do it right after incorporating the result
  • Max 8 concurrent remoras β€” beyond this, context overhead exceeds the gain
  • Never stack pilot fish β€” one at a time, no pilot fish spawning pilot fish
  • Spawn tasks ≀ 3 sentences β€” longer task descriptions need decomposition first
  • Enforcing the 30-Second Timeout

    The 30s cap isn't just a guideline β€” here's how to actually enforce it per runtime.

    OpenClaw subagents

    sessions_spawn({
      task: "...",
      mode: "run",
      runtime: "subagent",
      runTimeoutSeconds: 30   // hard kill after 30s β€” agent gets SIGTERM
    })
    
    runTimeoutSeconds is enforced by the OpenClaw runtime β€” the sub-agent process is killed if it exceeds it. Partial output is still returned.

    exec calls (shell, SSH, scripts)

    exec({
      command: "some-slow-command",
      timeout: 30,        // hard kill in seconds
      background: true,   // don't block the main agent turn
      yieldMs: 500        // poll back quickly to check
    })
    
    timeout kills the process. background: true means the main agent doesn't wait β€” it gets a session handle and can check back with process(poll).

    Gemini CLI via exec

    timeout 30 gemini -p "task here"
    

    or on Windows:

    Start-Process gemini -ArgumentList '-p "task"' -Wait -Timeout 30
    Wrap the CLI invocation with OS-level timeout / Start-Process -Timeout.

    Pilot fish β€” always use runTimeoutSeconds

    sessions_spawn({
      task: "pre-analyse partial results, draft structure, flag gaps",
      mode: "run",
      runTimeoutSeconds: estimatedRemainingMs / 1000,  // die before the last remora
    })
    
    Set it to *slightly less* than your estimated remaining wait β€” so the pilot fish always finishes before you need to synthesise.

    What happens when timeout fires

  • Sub-agent/process is killed
  • Whatever output was produced so far is returned
  • Main agent treats it as a partial result β€” still useful for synthesis
  • Log: [timeout] in the progress bar instead of βœ…
  • βŠ™ [A] slow task    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ ⏱ 30s [timeout β€” partial result]
    

    The LLM turn itself

    You can't hard-kill an LLM mid-turn, but you can: 1. Keep prompts tight β€” don't ask for exhaustive analysis in one turn 2. Use thinking: "none" for fast sub-tasks that don't need deep reasoning 3. Break large tasks into smaller shark-able chunks upfront

    Rule of thumb: if a task description is >3 sentences, it probably needs to be split into remoras.

    Compatibility β€” Claude, Codex, Gemini CLI

    The Shark Pattern is runtime-agnostic. remoras can be any agent type.

    OpenClaw (Claude / Sonnet / Opus)

    sessions_spawn({
      task: "...",
      mode: "run",
      runtime: "subagent",
      runTimeoutSeconds: 30   // hard cap for pilot fish
    })
    

    Codex

    sessions_spawn({
      task: "...",
      runtime: "acp",
      agentId: "codex",
      mode: "run",
      runTimeoutSeconds: 30
    })
    

    Codex-specific lifecycle:

  • Spawn with spawn_agent(...) or the runtime-equivalent remora launcher
  • Check completion with wait_agent(...)
  • If you want to reuse the same remora, send more work with send_input(...)
  • Otherwise, once the remora has completed and you've incorporated its result, call close_agent(id) so the agent does not linger in the session
  • Gemini CLI

    Gemini CLI is a local process β€” spawn via exec with a timeout:
    exec({
      command: "gemini -p \"task description here\"",
      timeout: 30,            // hard cap in seconds
      background: true,       // don't block main agent
      yieldMs: 500            // check back quickly
    })
    
    For Gemini sub-tasks, use exec with timeout + background: true rather than sessions_spawn. Treat the process handle the same way β€” continue working, collect output when it lands.

    Mixed fleets

    You can mix runtimes in the same shark run:
    spawn remora A β†’ Codex (coding task)
    spawn remora B β†’ Gemini (web search / analysis)
    spawn remora C β†’ Claude subagent (reasoning)
    spawn pilot fish  β†’ Claude subagent (pre-analysis, time-bounded)
    

    Which to use when

    | Task type | Best runtime | |-----------|-------------| | Code generation / editing | Codex | | Web search / summarise | Gemini CLI | | Multi-step reasoning | Claude subagent | | File ops / SSH / shell | exec (background) | | Pre-analysis / drafting | Claude subagent (pilot fish) |

    shark-exec Sub-Skill

    For slow shell commands (>5s), use the shark-exec companion skill:

  • Located at shark-exec/SKILL.md in this repo
  • Wraps any exec call in background + cron poller
  • Guarantees main turn completes in <30s even for 10-minute commands
  • Use it instead of inline exec whenever the command might block
  • Loop Enforcement (Ralph-style)

    The 30-second rule is best enforced at the shell level, not inside a turn.

    Use shark.sh (or shark.ps1 on Windows) to run Claude in a bounded loop:

    ./shark.sh "find the latest ChatterPC version, check pve3, summarise GitHub issues"
    

    Each iteration: 1. Builds a fresh prompt: skill context + task + current state 2. Runs claude --print with a hard timeout 25s shell wrapper 3. If Claude times out β†’ loop continues (it's expected β€” shark pattern means short turns) 4. If Claude writes .shark-done β†’ loop exits

    This is identical to the Ralph Loop pattern, but with the Shark Pattern as the prompt β€” Claude spawns remoras for slow work, keeps each turn under 25s, and the shell loop enforces the hard cut.

    When to use the loop vs direct claude

    | Use case | Approach | |----------|----------| | Single fast task (<30s total) | claude --print "..." directly | | Multi-step task, slow tools | ./shark.sh "..." loop | | CI/build watching | shark-exec (background + cron) | | Interactive chat | OpenClaw main session |

    Environment variables

    | Variable | Default | Description | |----------|---------|-------------| | SHARK_MAX_LOOPS | 50 | Maximum iterations before giving up | | SHARK_LOOP_TIMEOUT | 25 | Per-turn timeout in seconds (hard kill) |

    Completion protocol

    When Claude determines the task is done, it writes to .shark-done:

    TASK_COMPLETE
    
    
    The loop detects this file and exits cleanly.

    Commands

    When the user invokes these commands, follow the instructions for each.

    /shark

    Apply the Shark Pattern to the given task. Decompose, spawn remoras for slow ops, keep the main fin moving. Follow all rules in this SKILL.md.

    /shark-loop [--max-loops N] [--timeout S]

    Run the external shark loop enforcer. Execute:

    $env:SHARK_MAX_LOOPS = ""
    $env:SHARK_LOOP_TIMEOUT = ""
    powershell.exe -ExecutionPolicy Bypass -File "/shark.ps1" ""
    
    Defaults: --max-loops 50, --timeout 25. On Linux/Mac use shark.sh instead.

    /shark-status

    Check current shark state: 1. Read /shark-exec/state/pending.json β€” report active background jobs (label, command, elapsed time, whether overdue past maxSeconds) 2. If .shark-done exists, show its contents 3. If SHARK_LOG.md exists, show the last 10 lines 4. If nothing exists, report "No active shark jobs."

    /shark-clean

    Remove shark state files: .shark-done, SHARK_LOG.md, shark-exec/state/pending.json. Report what was cleaned.

    /shark-autotune

    Analyse timing history and recommend optimal settings.

    1. Read /state/timings.jsonl β€” each line is:

       {"ts":1710000000,"loop":1,"elapsed_s":12.3,"timeout_s":25,"result":"ok|timeout|done","task_hash":"abc123"}
       

    2. If no data, report "No timing data yet. Run tasks with /shark first."

    3. Compute and report: - Total runs (unique task_hash values) and total loops - Median turn time (p50) and p95 turn time - Timeout rate β€” % of turns with result "timeout" - Loops to completion β€” median and max (count loops per task_hash that has a "done" entry) - Wasted headroom β€” sum of (timeout_s - elapsed_s) for result "ok" turns - Optimal timeout β€” p95 turn time + 3s buffer, rounded up to nearest 5s - Optimal max_loops β€” p95 loops-to-completion + 2

    4. Show recommendations:

       Current:     SHARK_LOOP_TIMEOUT=25  SHARK_MAX_LOOPS=50
       Recommended: SHARK_LOOP_TIMEOUT=N   SHARK_MAX_LOOPS=M

    Rationale: - p95 turn time is Xs, so timeout of Ns covers 95% with buffer - p95 completion is N loops, so max_loops of M gives safe margin - Timeout rate is X% β€” [>15%: consider splitting tasks | healthy] - Wasted headroom: Xs total

    5. If timeout rate > 30%: "Consider breaking tasks into smaller steps." 6. If median turn time < 5s: "Most turns complete fast. Consider lowering timeout."

    Timing Instrumentation

    Both shark.sh and shark.ps1 automatically record per-loop timings to state/timings.jsonl. Each entry includes:

  • ts β€” Unix timestamp
  • loop β€” loop iteration number
  • elapsed_s β€” actual wall-clock seconds for this turn
  • timeout_s β€” configured timeout for this run
  • result β€” "ok" (completed), "timeout" (hit limit), "done" (task finished)
  • task_hash β€” 8-char hash correlating loops within a single run
  • Use /shark-autotune to analyse this data and tune your settings.


    References

  • Ralph Loop (sequential baseline): ghuntley.com/ralph/
  • OpenClaw sessions_spawn docs: spawn with mode: "run", runtime: "subagent"
  • Gemini CLI: npm install -g @google/gemini-cli
  • The name: sharks use ram ventilation β€” they literally die if they stop moving
  • πŸ’‘ Examples

    // remoras A (fast) and B (slow) both spawned
    // A finishes in 10s, B will take another 30s

    // Spawn pilot fish with 25s budget: sessions_spawn({ task: "Pre-analyse these results from remora A. Validate the data, note any gaps, draft the structure of the final report. Stop after 25 seconds.", runTimeoutSeconds: 25, mode: "run" })

    // Main agent continues doing other work // When B finishes β†’ kill pilot fish β†’ synthesise A + B + pilot draft