skills/docs/adr/006-nim-worker-cli.md
dan daec0f3b85 docs: ADR-006 Nim language choice for worker CLI
Documents rationale for using Nim with ORC for the worker coordination
CLI: fast startup, single binary, Python-like syntax, excellent SQLite
support via tiny_sqlite, CLI generation via cligen.

Closes skills-q40

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 23:27:32 -08:00

4.6 KiB

ADR-006: Nim for Worker Coordination CLI

Status

Accepted

Context

The multi-agent orchestration system needs a CLI tool (worker) for:

  • Spawning worker workspaces with git worktrees
  • State machine transitions (ASSIGNED → WORKING → IN_REVIEW → APPROVED → COMPLETED)
  • Heartbeat monitoring for stuck agent detection
  • SQLite-based message bus for coordination
  • Review-gate integration for quality enforcement

Requirements:

  • Fast startup (<10ms) for CLI commands run frequently
  • Single static binary for deployment simplicity
  • SQLite with WAL mode for concurrent access
  • Background thread for heartbeat without shared state complexity
  • Easy iteration during development

Decision

Use Nim with ORC memory management for the worker CLI.

Core Dependencies

Package Purpose
tiny_sqlite SQLite wrapper with RAII, statement caching
cligen Auto-generates CLI from proc signatures
std/json JSON parsing for context files

Why Nim

Performance:

  • ~1ms startup time (vs ~50ms Python, ~20ms Go)
  • Compiles to native binary via C backend
  • No runtime initialization overhead

Developer Experience:

  • Python-like syntax, low learning curve
  • REPL available for prototyping (nim secret)
  • Compile-time checks catch errors early
  • Excellent error messages

Memory Management:

  • ORC (Ownership + Reference Counting) handles cycles automatically
  • No manual memory management needed
  • Deterministic cleanup via destructors
  • Thread-safe without shared state via channels

SQLite Integration:

  • tiny_sqlite provides clean RAII semantics
  • Statement caching for performance
  • Proper NULL handling via Option types
  • WAL mode works correctly with multiple connections

CLI Generation:

  • cligen generates CLI from proc signatures
  • Help text from doc comments
  • Type-safe argument parsing
  • Subcommands via dispatchMulti

Example:

proc spawn(taskId: string, description = "", fromBranch = "origin/integration") =
  ## Create a new worker workspace
  ...

when isMainModule:
  dispatchMulti(
    [spawn, help = {"taskId": "Task identifier"}]
  )

Alternatives Considered

Python:

  • Slow startup (~50ms) unacceptable for frequent CLI calls
  • Packaging complexity (venv, dependencies)
  • Would work for prototype but not production

Go:

  • Faster startup than Python (~20ms) but still slower than Nim
  • More verbose syntax
  • Good SQLite support via mattn/go-sqlite3
  • Reasonable choice but Nim is more pleasant to write

Rust:

  • Excellent performance and safety
  • Steep learning curve slows iteration
  • Borrow checker friction for exploratory code
  • Good for later rewrite if needed

Bash:

  • Used for review-gate (simpler requirements)
  • Inadequate for state machine and SQLite operations
  • Error handling is painful

Build Configuration

# config.nims
switch("mm", "orc")           # Memory management
switch("threads", "on")       # Background heartbeat thread
switch("d", "release")        # Optimizations
switch("opt", "size")         # Smaller binary
switch("passL", "-lsqlite3")  # Link SQLite

Build command:

nim c -d:release src/worker.nim

Architecture

src/
├── worker.nim        # CLI dispatch, command implementations
└── worker/
    ├── types.nim     # Shared types, constants, error types
    ├── db.nim        # SQLite operations, message bus
    ├── state.nim     # State machine transitions
    ├── git.nim       # Worktree operations
    ├── context.nim   # Worker context file handling
    ├── heartbeat.nim # Background heartbeat thread
    ├── review.nim    # Review-gate integration
    └── utils.nim     # Common helpers

Consequences

Positive

  • Sub-millisecond startup enables responsive CLI
  • Single binary simplifies deployment
  • ORC eliminates memory management burden
  • cligen reduces CLI boilerplate to near-zero
  • Python-like syntax enables fast iteration
  • Type safety catches bugs at compile time

Negative

  • Smaller ecosystem than Python/Go/Rust
  • Fewer developers familiar with Nim
  • Some library quality variance
  • Compiler occasionally has edge case bugs

Mitigations

  • Pin Nim version (2.2.x) for stability
  • Use well-maintained libraries (tiny_sqlite, cligen)
  • Keep modules small and focused for maintainability
  • Document patterns for future contributors

References