payload.model, applies AI-WITH-YOU gates for autopilot-typed jobs (but NOT plain cron), and routes external work to other CLIs (Gemini, Codex, Claude CLI, Qwen, opencode) through delegate_to_cli.
Cron job lifecycle — dual-engine
Routing logic atcron/executor.mjs:260, 273, 283-290. Three-tier model resolution: payload.model > user_preferences[stage_key] > BACKGROUND_MODEL_DEFAULTS. ✓ VERIFIED
Cancellation contract
Engine-branched cancellation lives incron-service.mjs:1825-1862:
_runningChildren map shape is mutually exclusive — exactly one of process or abortController is set per entry. The synchronous aborted=true flag prevents races with in-flight emit calls. ✓ VERIFIED
Per-task model preferences
Fiveuser_preferences keys, read once at sidecar startup (restart-to-take-effect):
| Key | Default (BACKGROUND_MODEL_DEFAULTS) | Fallback | Consumed by |
|---|---|---|---|
heartbeat_model | google/gemini-2.5-flash | claude-haiku-4-5-20251001 | cron/heartbeat.mjs |
enrichment_model | google/gemini-2.5-flash | claude-haiku-4-5-20251001 | cron/enrichment-pipeline.mjs |
consolidation_model | google/gemini-2.5-flash | claude-haiku-4-5-20251001 | cron/consolidation-engine.mjs |
dream_model | google/gemini-2.5-flash | claude-haiku-4-5-20251001 | future BRS-memory-evolution Phase 4 |
cron_default_model | claude-haiku-4-5-20251001 | claude-sonnet-4-6 | cron/executor.mjs (when payload.model omitted) |
config/constants.mjs:80-97. Resolution via lib/preferences.mjs:69. ✓ VERIFIED
Heartbeat fallback chain
runHeartbeatWithFallback in heartbeat.mjs:881-891 triggers automatic Claude Haiku retry on two failure modes:
- Pi auth / safety errors — matched by predicate
isHeartbeatFallbackErrorat L797-806:/no model found|API key|unauthorized|no auth/i - Empty response —
EMPTY_QUERY_RESULTsentinel frompi-query-lifecycle.mjs:124(typically Gemini safety filter)
effort from payload (Pi-specific field) at L906 because Claude doesn’t understand it. ✓ VERIFIED
Recipe-mode guard
Recipe-mode jobs (payload.executionMode === 'recipe') are always routed to Claude SDK regardless of payload.model. Setting model: 'google/gemini-2.5-flash' on a recipe job is silently overridden at executor.mjs:273. Recipes require SDK behavior. ✓ VERIFIED
Autopilot — three-table model & 3-layer gate
Autopilot is the AI-WITH-YOU gate enforcement engine for background work. It splits “what” / “when” / “proposal” across three tables (migration 0046 C2):The three enforcement layers
| Layer | What it enforces | Where |
|---|---|---|
| L1 — DDL CHECK | acceptance_mode IN ('suggest','auto') + DEFAULT ‘suggest’ | Migration 0045 |
| L2 — Runtime JS | Branch on acceptance_mode; suggest → proposal only | autopilot/runner.mjs:386-403, 412-514 |
| L3 — Rust command guard | assert_auto_opt_in rejects auto without explicit_user_opt_in=true | autopilots.rs:172-185 |
CCW multi-CLI bus
Orion delegates external work to other CLIs via two MCP tools inmcp__orion-tools:
| Tool | Mode | What it does |
|---|---|---|
delegate_to_cli | Fire-and-forget | Returns immediately with delegation ID; background work continues |
execute_cli_inprocess | Awaiting | Blocks until CLI exits; returns the output |
ccw/cli/executor.mjs::execute(), which spawns the chosen CLI subprocess.
GAP-D4: Claude CLI deliberately inherits user’s
~/.claude/.executor.mjs:645-649 explicitly strips CLAUDE_CONFIG_DIR from the spawned Claude CLI’s env. The comment documents this is intentional: Claude CLI falls back to ~/.claude/ (user’s own Claude Code config), not Orion’s runtime config (prompts/).This means:- User’s personal skills, hooks, settings.json, MCP servers — all load into the spawned Claude CLI.
- Spawned CLI uses user’s OAuth tokens (not Orion’s Cloudflare-Worker-proxied tokens).
- Billing flows through user’s account, not Orion’s.
CLAUDE_CONFIG_DIR=prompts/ keeps user config out of Orion runtime). When Orion delegates to Claude CLI, the CLI operates with the user’s full personal context. Users should know that “delegate to Claude CLI” is fundamentally different from “Orion’s Claude SDK” in trust posture.Cron model selection guide
- Pick Pi (Gemini Flash)
- Pick Claude (Sonnet)
- Pick Claude Haiku
- Routine summary, digest, scan, monitor
- Cost-sensitive jobs running many times/day
- Local-only file ops in vault
- Ambient: heartbeat, enrichment, consolidation
- ~36× cheaper than Sonnet per run
Key files
src-tauri/sidecar/cron/executor.mjs
src-tauri/sidecar/cron/executor.mjs
executeIsolated dispatcher, three-tier model resolution at L283-290, recipe-mode guard at L273, autopilot short-circuit at L260, 10s watchdog, VAL-007 post-settlement guard.src-tauri/sidecar/cron/cron-service.mjs
src-tauri/sidecar/cron/cron-service.mjs
_runningChildren map at L243, cancelJob engine-branched cancellation at L1825-1862.src-tauri/sidecar/cron/heartbeat.mjs
src-tauri/sidecar/cron/heartbeat.mjs
isHeartbeatFallbackError predicate L797-806, runHeartbeatWithFallback L881-891.src-tauri/sidecar/autopilot/runner.mjs
src-tauri/sidecar/autopilot/runner.mjs
_fireSuggestPath L386-403 (proposed-only), _fireAutoPath L412-514 (atomic txn).src-tauri/src/commands/autopilots.rs
src-tauri/src/commands/autopilots.rs
assert_auto_opt_in Rust guard L172-185. Callers at L453 (create), L508 (update). Lock-in tests at L749-761.src-tauri/sidecar/ccw/orion-tools-mcp-server.mjs
src-tauri/sidecar/ccw/orion-tools-mcp-server.mjs
delegate_to_cli L725-750. execute_cli_inprocess L773-823.src-tauri/sidecar/ccw/cli/executor.mjs
src-tauri/sidecar/ccw/cli/executor.mjs
buildSpawnEnv 28-key strip L128-175. spawn() with no sandbox wrapper L505-700. Claude CLI deliberate non-override L645-649.src-tauri/sidecar/config/constants.mjs
src-tauri/sidecar/config/constants.mjs
BACKGROUND_MODEL_DEFAULTS/FALLBACKS L80-97. BACKGROUND_MODEL_STAGE_KEYS L204-235.Next
Trust → Known Gaps
The consolidated safety appendix. All gaps from every page with severity, evidence, fix paths, and links to filed issues.