-
Notifications
You must be signed in to change notification settings - Fork 2
Architecture
lacause edited this page Mar 29, 2026
·
3 revisions
How OCC works under the hood.
┌──────────────────┐ ┌────────────────────┐
│ Claude Code │──MCP──▶│ MCP Server │
│ Claude Desktop │ stdio │ (index.ts) │
└──────────────────┘ │ 25 tools exposed │
└────────┬───────────┘
│
┌──────────────────┐ ┌────────▼───────────┐
│ curl / Browser │──HTTP──▶│ REST Server │
│ Any client │ :4242 │ (rest.ts) │
└──────────────────┘ │ 30+ endpoints │
│ SSE streaming │
└────────┬───────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Loader │ │ Executor │ │ Scheduler │
│ (loader.ts) │ │(executor.ts) │ │(scheduler.ts)│
│ │ │ │ │ │
│ YAML parsing │ │ Chain runner │ │ Cron jobs │
│ Zod validation│ │ Process mgmt │ │ Auto-execute │
│ Dep. graph │ │ Caching │ │ │
└──────┬───────┘ └──────┬───────┘ └──────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│ chains/ │ │ Claude CLI │
│ (YAML) │ │ (subprocess) │
└──────────┘ └──────────────┘
-
YAML parsing —
js-yamlconverts YAML to JavaScript objects - Zod validation — Every field validated against strict schema
- Dependency graph — Topological sort (Kahn's algorithm) produces execution waves
Chain YAML → js-yaml → Zod safeParse → buildDependencyGraph → waves[]
Waves are groups of steps with no dependencies between them:
Wave 1: [step_a, step_b, step_c] ← parallel
Wave 2: [step_d] ← depends on a,b
Wave 3: [step_e, step_f] ← depends on d
The core execution loop:
for each wave in dependency_graph:
await Promise.all(
wave.map(step => executeStep(step))
)
Step execution flow:
1. Check condition → skip if false
2. Check cache → return cached if valid
3. Run pre-tools → inject variables
4. Resolve {variables} in prompt
5. Spawn Claude CLI process
6. Stream stdout → SSE events
7. Validate output (guardrails)
8. Store result in variables map
9. Save to cache if enabled
10. Persist execution state
Process management:
- Each step spawns
claude --print --output-format stream-json - Timeout: configurable per-step, default 5 minutes
- Escalation: SIGTERM → wait 3s → SIGKILL
- Heartbeat: every 30s, checks if process is still alive
- Cancellation: kills all active processes for an execution
Concurrency control:
- Max 5 concurrent executions (configurable)
- Returns HTTP 429 when exceeded
- Each execution tracks its active child processes
Express server with:
- JSON + YAML body parsing (2MB limit)
- CORS enabled (all origins)
- SSE streaming with 30s heartbeat
- Static file serving for optional frontend
- Uses
node-cronfor cron expression parsing - Schedules persisted to JSON file
- Loads on startup, restores active schedules
- Toggle on/off without deleting
Similar to chain executor but at the chain level:
- Load all referenced chains
- Build dependency graph between chains
- Execute chains in waves
- Pass outputs between chains via variable mapping
Variables flow through the system:
Chain Input: { topic: "AI" }
↓
Variables Map: { "input.topic": "AI", "topic": "AI" }
↓
Step 1 runs → output = "Research findings..."
↓
Variables Map: { "input.topic": "AI", "research": "Research findings..." }
↓
Step 2 prompt: "Summarize: {research}" → "Summarize: Research findings..."
Executions → executions.json (last 200, purged after 7 days)
Schedules → schedules.json
Cache → cache/{chain_name}/{hash}.json (TTL-based)
Pipelines → pipeline-executions.json (last 50)
All persistence is file-based (no database required). Files are written asynchronously (non-blocking).
OCC Server (Node.js)
├── Express HTTP server (single thread)
├── MCP stdio transport (single connection)
├── Scheduler (node-cron timers)
└── Active executions
├── Execution A
│ ├── Step 1 → claude process (PID 1234)
│ └── Step 2 → claude process (PID 1235)
└── Execution B
└── Step 1 → claude process (PID 1236)
Each step is a separate claude CLI process. Node.js manages process lifecycle, collects output, and streams events.
| Scenario | Recovery |
|---|---|
| Step timeout | SIGTERM → 3s → SIGKILL, step marked error |
| Process crash | Heartbeat detects within 30s, step marked error |
| Server restart | Orphaned "running" executions marked error on load |
| Invalid YAML | Rejected at load time with Zod error details |
| Claude CLI missing | Detected at startup, clear error message |
| Execution cancelled | All child processes killed, pending steps skipped |
| Cache corruption | Silent fallback to re-execution |
- Configuration — All environment variables
- Token Optimization — How architecture reduces cost
- REST API — HTTP endpoint reference