Event System
BoatmanMode emits structured JSON events to stdout during workflow execution, enabling real-time integration with external tools like the Boatman Desktop application.
Overview
Events are emitted as newline-delimited JSON (NDJSON) to stdout, allowing real-time tracking of the multi-agent orchestration workflow. Each event represents a significant state change.
Event Format
All events follow this JSON structure:
{
"type": "string", // Event type (required)
"id": "string", // Unique identifier (optional)
"name": "string", // Human-readable name (optional)
"description": "string", // Detailed description (optional)
"status": "string", // "success" or "failed" (optional)
"message": "string", // Progress message (optional)
"data": {} // Additional metadata (optional)
}Event Types
agent_started
Emitted when an agent begins execution.
{
"type": "agent_started",
"id": "planning-ENG-123",
"name": "Planning & Analysis",
"description": "Analyzing codebase and creating implementation plan"
}agent_completed
Emitted when an agent finishes execution.
{
"type": "agent_completed",
"id": "planning-ENG-123",
"name": "Planning & Analysis",
"status": "success"
}progress
General progress message not tied to a specific agent.
{
"type": "progress",
"message": "Review & refactor iteration 2 of 3"
}task_created (Reserved)
Not currently emitted by BoatmanMode, reserved for future use by the event system for external integrations.
task_updated (Reserved)
Not currently emitted by BoatmanMode, reserved for future use.
Agent ID Format
Agent IDs follow a consistent pattern for tracking:
| Step | Agent ID Pattern | Example |
|---|---|---|
| Prepare | prepare-{taskID} | prepare-ENG-123 |
| Worktree | worktree-{taskID} | worktree-ENG-123 |
| Planning | planning-{taskID} | planning-ENG-123 |
| Preflight | preflight-{taskID} | preflight-ENG-123 |
| Execute | execute-{taskID} | execute-ENG-123 |
| Test | test-{taskID} | test-ENG-123 |
| Review | review-{iteration}-{taskID} | review-1-ENG-123 |
| Refactor | refactor-{iteration}-{taskID} | refactor-2-ENG-123 |
| Commit | commit-{taskID} | commit-ENG-123 |
| PR | pr-{taskID} | pr-ENG-123 |
Example Event Flow
A typical event sequence for a successful workflow:
{"type":"agent_started","id":"prepare-ENG-123","name":"Preparing Task"}
{"type":"agent_completed","id":"prepare-ENG-123","name":"Preparing Task","status":"success"}
{"type":"agent_started","id":"worktree-ENG-123","name":"Setup Worktree"}
{"type":"agent_completed","id":"worktree-ENG-123","name":"Setup Worktree","status":"success"}
{"type":"agent_started","id":"planning-ENG-123","name":"Planning & Analysis"}
{"type":"agent_completed","id":"planning-ENG-123","name":"Planning & Analysis","status":"success"}
{"type":"agent_started","id":"preflight-ENG-123","name":"Pre-flight Validation"}
{"type":"agent_completed","id":"preflight-ENG-123","name":"Pre-flight Validation","status":"success"}
{"type":"agent_started","id":"execute-ENG-123","name":"Execution"}
{"type":"agent_completed","id":"execute-ENG-123","name":"Execution","status":"success"}
{"type":"agent_started","id":"test-ENG-123","name":"Running Tests"}
{"type":"agent_started","id":"review-1-ENG-123","name":"Code Review #1"}
{"type":"agent_completed","id":"test-ENG-123","name":"Running Tests","status":"success"}
{"type":"agent_completed","id":"review-1-ENG-123","name":"Code Review #1","status":"failed"}
{"type":"progress","message":"Review & refactor iteration 1 of 3"}
{"type":"agent_started","id":"refactor-1-ENG-123","name":"Refactoring #1"}
{"type":"agent_completed","id":"refactor-1-ENG-123","name":"Refactoring #1","status":"success"}
{"type":"agent_started","id":"commit-ENG-123","name":"Commit & Push"}
{"type":"agent_completed","id":"commit-ENG-123","name":"Commit & Push","status":"success"}
{"type":"agent_started","id":"pr-ENG-123","name":"Create PR"}
{"type":"agent_completed","id":"pr-ENG-123","name":"Create PR","status":"success"}Consuming Events
Command Line
boatman work ENG-123 | grep '^{' | jqGo Integration
package main
import (
"bufio"
"encoding/json"
"log"
"os/exec"
)
type Event struct {
Type string `json:"type"`
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Status string `json:"status,omitempty"`
Message string `json:"message,omitempty"`
}
func main() {
cmd := exec.Command("boatman", "work", "ENG-123")
stdout, _ := cmd.StdoutPipe()
cmd.Start()
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
var event Event
if err := json.Unmarshal([]byte(line), &event); err == nil {
log.Printf("Event: %s - %s [%s]", event.Type, event.Name, event.Status)
}
}
cmd.Wait()
}Implementation Details
Events are emitted using the internal/events package:
import "github.com/philjestin/boatmanmode/internal/events"
// Start an agent
events.AgentStarted("execute-ENG-123", "Execution", "Implementing code changes")
// Complete an agent
events.AgentCompleted("execute-ENG-123", "Execution", "success")
// Progress update
events.Progress("Running tests...")All events are flushed to stdout immediately for real-time updates.
Design Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Output channel | stdout | Simple, works with standard pipes |
| Format | NDJSON | Streaming-friendly, standard format |
| Agent IDs | {step}-{taskID} | Unique, traceable, human-readable |
| Emission | Always on | Consistent behavior, easily filtered |
Performance Impact
- Synchronous emission (no goroutines needed)
- JSON marshaling < 1ms per event
- ~20-30 events per workflow execution
- Negligible overhead