.enki/events/. The coordinator polls this directory every 3 seconds, processes all signal files, and deletes them.
Why Signal Files?
Enki’s architecture separates concerns:- Workers are subprocesses running ACP agents (
claude-codeor compatible clients) - Coordinator is the main
enkiprocess running the orchestrator state machine - MCP server runs as a subprocess (
enki mcp) and can’t directly call into the coordinator
- No sockets, no shared memory, no complex IPC
- Workers write JSON, coordinator reads and deletes
- Fire-and-forget: workers don’t block waiting for responses
- Coordinator processes signal files in batches on its polling tick
This design trades real-time responsiveness (3s poll interval) for simplicity and robustness. Workers don’t need to maintain connections or handle backpressure.
Signal File Format
Signal files are written to.enki/events/sig-<id>.json where <id> is a unique ID generated by Id::new("sig").
Common Structure
All signal files are JSON objects with atype field:
type field and dispatches to the appropriate handler.
Signal File Lifecycle
- Write: Worker calls MCP tool → handler writes
.enki/events/sig-<random-id>.json - Poll: Coordinator wakes on 3s tick, reads all
sig-*.jsonfiles in directory - Process: Coordinator parses each signal, updates orchestrator state, produces events
- Delete: Coordinator deletes processed signal files
crates/cli/src/commands/mcp/handlers.rs:14-23
Signal File Types
Enki uses several signal types for different operations:execution_created
Written by:enki_execution_create tool
When: Planner creates a new multi-step execution
Payload:
crates/cli/src/commands/mcp/handlers.rs:241
task_created
Written by:enki_task_create tool, enki_task_retry tool
When: Planner creates a standalone task or retries a failed task
Payload:
crates/cli/src/commands/mcp/handlers.rs:71, handlers.rs:477
steps_added
Written by:enki_execution_add_steps tool
When: Planner dynamically adds steps to a running execution (e.g., after a checkpoint pause)
Payload:
crates/cli/src/commands/mcp/handlers.rs:390-393
pause
Written by:enki_pause tool
When: Planner pauses an execution or a single step
Payload (pause execution):
crates/cli/src/commands/mcp/handlers.rs:491-495
cancel
Written by:enki_cancel tool
When: Planner cancels an execution or step (kills running workers)
Payload (cancel execution):
crates/cli/src/commands/mcp/handlers.rs:507-511
stop_all
Written by:enki_stop_all tool
When: Planner or worker requests immediate halt of all running workers
Payload:
crates/cli/src/commands/mcp/handlers.rs:525
resume
Written by:enki_resume tool
When: Planner resumes a paused execution or step (e.g., after reviewing checkpoint output)
Payload (resume execution):
crates/cli/src/commands/mcp/handlers.rs:418-425
worker_report
Written by:enki_worker_report tool
When: Worker reports its current high-level activity (“analyzing codebase”, “running tests”, etc.)
Payload:
task.current_activity in DB, displayed in TUI
Source: crates/cli/src/commands/mcp/handlers.rs:535-539
enki_mail_send, enki_mail_reply tools
When: Worker or planner sends a message
Payload:
crates/cli/src/commands/mcp/handlers.rs:707-714, handlers.rs:832-839
Coordinator Polling Loop
The coordinator runs atokio::select! loop with a 3s tick:
- Signal files are batched (all processed in one tick)
process_events()may produce new events in a cascade (e.g., spawning a worker that immediately fails)- The coordinator drains events in a
while !events.is_empty()loop
When Signal Files Are Used
Signal files are used for state-changing operations that affect the orchestrator:- Creating tasks or executions
- Pausing, resuming, cancelling tasks
- Retrying failed tasks
- Adding steps to running executions
- Reporting worker activity
- Sending inter-agent mail
- Stopping all workers
- Read-only operations (
enki_status,enki_task_list) - Operations handled entirely by the DB (
enki_mail_readmarks a message as read in SQLite, no coordination needed)
Debugging Signal Files
Signal files are ephemeral (deleted after processing), but you can observe them:Design Rationale
Why not use a more sophisticated IPC mechanism? Simplicity:- No protocol negotiation, no versioning, no handshakes
- Workers don’t need to manage connections or handle failures
- Coordinator can restart without draining queues or re-establishing connections
- Workers crash? Signal files persist until coordinator processes them
- Coordinator crash? Signal files remain on disk for recovery (though current coordinator doesn’t implement recovery-on-restart)
- No risk of socket exhaustion, connection leaks, or backpressure
- Signal files are human-readable JSON
- Easy to inspect with
cat .enki/events/sig-*.json - Can replay signal files by copying them back to
.enki/events/
- 3s poll latency (not real-time)
- File I/O overhead (negligible for Enki’s workload)
- No guaranteed delivery if coordinator exits between polling cycles (acceptable for Enki’s single-session model)
