Skip to main content
The IPC (Inter-Process Communication) system enables bidirectional communication between workers and the dispatcher without the worker losing context. This is what allows workers to ask questions mid-task and continue working after receiving answers.

Overview

Dispatch uses a file-based IPC protocol with sequence-numbered files in .dispatch/tasks/<task-id>/ipc/:
.dispatch/tasks/security-review/ipc/
  001.question  ← Worker writes this
  001.answer    ← Dispatcher writes this
  001.done      ← Worker writes this (acknowledgment)
  002.question  ← Next question from worker
  002.answer    ← Dispatcher's answer
  002.done      ← Worker's acknowledgment
  .done         ← Worker writes this when ALL work is complete
Why file-based IPC? It’s simple, debuggable, works across different CLI environments, and requires no special infrastructure. You can inspect the conversation by just reading the files.

Question/Answer Flow

Here’s the complete flow when a worker needs to ask a question:

1. Worker Writes Question

The worker encounters a blocker and needs user input:
# Worker determines next sequence number (count of *.question files + 1)
SEQ=$(printf "%03d" $(($(ls .dispatch/tasks/<task-id>/ipc/*.question 2>/dev/null | wc -l) + 1)))

# Write question atomically (temp file + mv)
echo "Found 2 auth implementations. Which should I review?" > .dispatch/tasks/<task-id>/ipc/${SEQ}.question.tmp
mv .dispatch/tasks/<task-id>/ipc/${SEQ}.question.tmp .dispatch/tasks/<task-id>/ipc/${SEQ}.question
Result: .dispatch/tasks/<task-id>/ipc/001.question now exists.

2. Monitor Detects Question

The monitor script polls every 3 seconds:
# Monitor checks for unanswered questions
for q in "$IPC_DIR"/*.question; do
  seq=$(basename "$q" .question)
  [ ! -f "$IPC_DIR/${seq}.answer" ] && exit 0  # Exit when unanswered question found
done
When it finds 001.question without a matching 001.answer, the monitor exits.

3. Notification Triggers

The monitor’s exit triggers a <task-notification> in Claude Code. The dispatcher:
  1. Matches the notification’s task ID to its internal monitor task ID
  2. Realizes this is a monitor notification (not worker)
  3. Knows a question is waiting

4. Dispatcher Reads and Surfaces Question

# List IPC directory to find unanswered questions
$ ls .dispatch/tasks/security-review/ipc/
001.question

# Read the question
$ cat .dispatch/tasks/security-review/ipc/001.question
Found 2 auth implementations. Which should I review?
Dispatcher surfaces this to you:
Worker is asking: "Found 2 auth implementations. Which should I review?"

5. User Provides Answer

You respond:
Review auth.ts - it's the current implementation. legacy-auth.js is deprecated.

6. Dispatcher Writes Answer

The dispatcher writes your answer atomically:
# Write to temp file first
echo "Review auth.ts - it's the current implementation. legacy-auth.js is deprecated." > .dispatch/tasks/security-review/ipc/001.answer.tmp

# Atomic move
mv .dispatch/tasks/security-review/ipc/001.answer.tmp .dispatch/tasks/security-review/ipc/001.answer
Always use atomic writes (temp file + mv). Direct writes could result in the worker reading a partially-written file.

7. Dispatcher Respawns Monitor

The old monitor exited in step 2. The dispatcher spawns a new one:
bash /tmp/monitor--security-review.sh
# description: "Monitoring progress: security-review"
The script already exists from initial setup, so this is just re-running it.

8. Worker Detects Answer

The worker has been polling for 001.answer:
# Worker polling loop (every 2-3 seconds)
while [ ! -f ".dispatch/tasks/<task-id>/ipc/001.answer" ]; do
  sleep 2
  
  # Timeout check
  if [ $ELAPSED -ge 180 ]; then  # 3 minutes
    # Fallback: dump context, mark [?], exit
    echo "Context dump..." > .dispatch/tasks/<task-id>/context.md
    # Update plan file to mark item [?]
    exit 1
  fi
done

# Answer found! Read it
ANSWER=$(cat .dispatch/tasks/<task-id>/ipc/001.answer)

9. Worker Acknowledges and Continues

# Write acknowledgment
touch .dispatch/tasks/<task-id>/ipc/001.done

# Continue working with the answer
echo "Got answer: $ANSWER"
# ... continue processing checklist items ...
The worker never exited during this entire flow. All context is preserved.

File Naming Conventions

All IPC files live in .dispatch/tasks/<task-id>/ipc/:

Sequence-Numbered Files

File PatternWritten ByPurposeExample
NNN.questionWorkerQuestion for user001.question
NNN.answerDispatcherAnswer to question001.answer
NNN.doneWorkerAcknowledgment001.done
  • Sequence numbers are zero-padded to 3 digits: 001, 002, …, 099, 100, etc.
  • Sequences are 1-indexed (start at 001, not 000)
  • Sequences are monotonically increasing (no gaps or reuse)

Special Files

FileWritten ByPurpose
.doneWorkerCompletion marker - all checklist items finished
The leading dot in .done distinguishes it from sequence files and prevents accidental pattern matches.

Sequence Numbering Logic

The worker determines the next sequence number:
# Count existing .question files and add 1
COUNT=$(ls .dispatch/tasks/<task-id>/ipc/*.question 2>/dev/null | wc -l)
NEXT_SEQ=$(printf "%03d" $((COUNT + 1)))

# Result:
# 0 existing questions → 001
# 1 existing question  → 002
# 2 existing questions → 003

Atomic Write Pattern

Both worker and dispatcher must use atomic writes to prevent race conditions and partial reads.

Why Atomic Writes?

# Direct write - NOT ATOMIC
echo "This is my question about..." > 001.question
Problem: If the monitor polls while this write is happening, it might read:
This is my qu
Partial data causes errors or corrupted messages.

Implementation

#!/bin/bash
IPC_DIR=".dispatch/tasks/<task-id>/ipc"
SEQ="001"
QUESTION="Found 2 auth implementations. Which should I review?"

# Atomic write
echo "$QUESTION" > "$IPC_DIR/${SEQ}.question.tmp"
mv "$IPC_DIR/${SEQ}.question.tmp" "$IPC_DIR/${SEQ}.question"
On POSIX filesystems (Linux, macOS), mv is guaranteed atomic when source and destination are on the same filesystem. This is a kernel guarantee.

Timeout and Fallback Behavior

Worker-Side Timeout

If the worker doesn’t receive an answer within ~3 minutes:
START=$(date +%s)
TIMEOUT=180  # 3 minutes

while [ ! -f "$IPC_DIR/001.answer" ]; do
  sleep 2
  ELAPSED=$(( $(date +%s) - START ))
  
  if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
    # Timeout fallback
    echo "Fell back to context dump due to IPC timeout" >&2
    
    # Dump context
    cat > .dispatch/tasks/<task-id>/context.md << 'CONTEXT'
    ## Current State
    
    Working on checklist item: "Review authentication logic"
    
    ## Question
    
    Found 2 auth implementations. Which should I review?
    
    ## Context So Far
    
    - Scanned for secrets: found hardcoded API key in config.ts:47
    - Read auth.ts and legacy-auth.js
    - Both appear to be in use
    CONTEXT
    
    # Mark item as blocked in plan file
    # ... (edit plan.md to change [ ] to [?]) ...
    
    exit 1
  fi
done
Result:
  • Worker preserves its context in context.md
  • Plan file shows [?] marker with inline question
  • Worker exits
  • Dispatcher can spawn a new worker with the context when you answer
Why 3 minutes? Long enough to avoid false timeouts (you might be reading the question), short enough to prevent indefinite waiting if the dispatcher crashes.

Monitor-Side Timeout

If the monitor sees no activity for 30 minutes:
TIMEOUT=1800  # 30 minutes
START=$(date +%s)

while true; do
  # ... normal monitoring logic ...
  
  ELAPSED=$(( $(date +%s) - START ))
  if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
    echo "Monitor timeout after 30 minutes" >&2
    exit 1  # Error exit
  fi
  
  sleep 3
done
Result:
  • Monitor exits with error code
  • <task-notification> triggers
  • Dispatcher checks if worker is still running
  • Dispatcher reports timeout state to you
Why 30 minutes? Assumes genuinely long-running tasks (large codebases, thorough analysis) while catching truly stuck processes.

Fallback Flow

When worker timeout triggers: Key difference: New worker instance means context is reconstructed from files, not preserved in memory.

Directionality Rules

IPC is worker-initiated only. This is a critical constraint:

✅ Allowed: Worker → Dispatcher

  • Worker writes .question files
  • Dispatcher reads .question files
  • Dispatcher writes .answer files
  • Worker reads .answer files

❌ Forbidden: Dispatcher → Worker (unsolicited)

  • Dispatcher cannot initiate communication
  • Dispatcher cannot write files the worker isn’t polling for
  • Worker only polls for .answer files matching its own .question files
  • Random files in IPC directory will be ignored by the worker

Adding Context to Running Workers

If you need to provide additional context to a running worker, append to the plan file, not the IPC directory:
# Security Review

- [x] Scan for hardcoded secrets
- [ ] Review authentication logic
- [ ] Check session management
- [ ] Write findings report

> **Note from dispatcher:** The admin panel uses legacy-auth.js. Make sure to check both auth.ts and legacy-auth.js.
Workers read the plan file as they progress, so they’ll see notes before reaching subsequent items.
Writing unsolicited files to ipc/ has no effect. The worker won’t see them because it’s not polling for them.

Startup Reconciliation

If the dispatcher session restarts (e.g., you close and reopen Claude Code), it should check for orphaned questions:
# Find all active tasks
for task_dir in .dispatch/tasks/*/; do
  task_id=$(basename "$task_dir")
  ipc_dir="${task_dir}ipc"
  
  # Check for unanswered questions
  for q in "$ipc_dir"/*.question; do
    [ ! -f "$q" ] && continue  # No questions
    
    seq=$(basename "$q" .question)
    answer="${ipc_dir}/${seq}.answer"
    
    if [ ! -f "$answer" ]; then
      # Found unanswered question
      echo "Task $task_id has unanswered question: $seq"
      question_text=$(cat "$q")
      
      # Surface to user, wait for answer, write answer, respawn monitor
      # ... (same flow as step 4-7) ...
    fi
  done
done
This ensures questions are never silently lost even if the dispatcher crashes or is restarted.

Complete Example

Here’s a full IPC exchange for a task:
1

Worker Asks First Question

# Worker writes 001.question
echo "Found 2 auth implementations. Which to review?" > ipc/001.question.tmp
mv ipc/001.question.tmp ipc/001.question
Worker polls for 001.answer
2

Monitor Detects, Exits

# Monitor finds unanswered question
[ ! -f ipc/001.answer ] && exit 0
<task-notification> fires.
3

User Answers

Dispatcher: “Worker is asking: Found 2 auth implementations. Which to review?”You: “Review auth.ts”
4

Dispatcher Writes Answer, Respawns Monitor

echo "Review auth.ts" > ipc/001.answer.tmp
mv ipc/001.answer.tmp ipc/001.answer
bash /tmp/monitor--<task-id>.sh &
5

Worker Receives, Acknowledges

# Worker's poll succeeds
answer=$(cat ipc/001.answer)  # "Review auth.ts"

# Write acknowledgment
touch ipc/001.done.tmp
mv ipc/001.done.tmp ipc/001.done

# Continue working...
6

Worker Asks Second Question (Later)

echo "auth.ts uses JWT. Should I audit the token validation logic?" > ipc/002.question.tmp
mv ipc/002.question.tmp ipc/002.question
Same flow repeats for 002.answer, 002.done
7

Worker Completes All Items

# All checklist items done
touch ipc/.done.tmp
mv ipc/.done.tmp ipc/.done
Monitor detects .done, exits cleanly. Task complete!

Final IPC Directory State

.dispatch/tasks/security-review/ipc/
  001.question
  001.answer
  001.done
  002.question
  002.answer
  002.done
  .done
You can read this directory like a conversation log:
$ cat ipc/001.question
Found 2 auth implementations. Which to review?

$ cat ipc/001.answer  
Review auth.ts

$ cat ipc/002.question
auth.ts uses JWT. Should I audit the token validation logic?

$ cat ipc/002.answer
Yes, focus on signature verification and expiration checks.

$ ls ipc/.done
ipc/.done  # Task complete

Technical Benefits

Context Preservation

Workers stay alive during Q&A, preserving all in-memory state, file handles, and analysis results.

Debuggability

All communication is plain text files. Just cat them to see what happened.

No Infrastructure

No message queues, databases, or network protocols. Works anywhere with a POSIX filesystem.

Race-Condition Free

Atomic writes via mv eliminate partial reads and write conflicts.

Timeout Safety

Automatic fallback prevents indefinite hangs while allowing reasonable response time.

Recoverable

Startup reconciliation ensures questions survive dispatcher restarts.

Next Steps

Architecture

See how IPC fits into the overall system design

How It Works

Understand the complete execution flow

Build docs developers (and LLMs) love