Data Model
Single SQLite file: /var/lib/orchestra/state/orchestra.db
Tables
mas_tasks — inbound work queue
| Column | Notes |
|---|---|
id | source identifier — STU-806, gh-pr-1234, cron-maintainer-... |
source | linear | github | slack | scheduled | maintainer | handoff | sentry | langfuse | developer. maintainer is used when the dispatcher routes approved proposals; handoff when an agent-delegated handoff creates a new task for a target agent; sentry/langfuse for observability webhook dispatches |
source_event_id | idempotency key (UNIQUE); NULL for handoff tasks (no external event to dedup) |
source_meta_json | source-specific back-channel data (Linear: issue_id, team_id, urls, creator_id, creator_name, creator_email); the listener resolves the Linear issue creator via API before the delegator spawns; used by the dispatcher for Slack @-mention resolution; for handoff tasks: {"origin_task_id": "<id>", "origin_agent": "<name>"}; for scheduled manual triggers: {"operator_comment": "<text>"} passed via run-scheduled-agent.sh --comment |
status | pending → running → done | failed | waiting_for_human | skipped; skipped is set by run-scheduled-agent.sh when a timer fires but a prior invocation of the same agent is still active |
plan_json | full dispatch plan; the listener writes a placeholder row before the delegator spawns, then the delegator fills in the plan via UPDATE; validated by the dispatcher |
proposal_json | maintainer proposal JSON (source=maintainer only) |
created_at / updated_at | timestamps |
error | set on failure; also set for skipped rows with the overlap reason |
human_tasks — agent→human questions
| Column | Notes |
|---|---|
id | uuid |
mas_task_id | FK → mas_tasks |
agent | which agent is blocked |
question | short headline for the TUI list |
payload_json | structured AskUserQuestion payload (1–4 questions + options); free-text answers are mapped to human-readable question text by the listener using this field |
status | open → approved | declined | answered → consumed |
answer | JSON object keyed by question header (short label), with full question text as fallback for backward compatibility |
slack_ts / slack_channel | for updating the Slack message |
created_at / resolved_at | timestamps |
running_workers — crash recovery
| Column | Notes |
|---|---|
plan_id + worker_id | composite PK |
pid | OS pid; checked on dispatcher restart |
started_at | timestamp |
workspace | worker workspace path |
session_id | claude session uuid; captured from the stream-json init event; used for --resume |
agent | agent role name (e.g. developer) |
status | running → done | failed | waiting_for_human | retry_requested; orphaned workers on dispatcher restart are marked retry_requested instead of failed so dispatchRetries() auto-resumes them |
seen_events — webhook dedup
event_id + received_at. Purged after 24h by the dispatcher on startup and by the listener every 6h.
Agent state files
Outside the database, some agents persist state as plain files:
| File | Written by | Purpose |
|---|---|---|
${ORCHESTRA_STATE_DIR:-/var/lib/orchestra/state}/documenter-last-sha.txt | documenter | Last processed commit SHA for daily doc updates |
${ORCHESTRA_STATE_DIR:-/var/lib/orchestra/state}/auditor-last-sha.txt | auditor | Last processed commit SHA for weekly security scans |
${ORCHESTRA_STATE_DIR:-/var/lib/orchestra/state}/maintainer-last-sha.txt | maintainer | Last processed commit SHA for commit-delta context |
${ORCHESTRA_STATE_DIR:-/var/lib/orchestra/state}/maintainer-acceptance.txt | maintainer | Cumulative proposal acceptance-rate metrics (total, approved) broken down by target agent and priority band; serves as a calibration signal when drafting proposals; bootstrapped with zeros on first run |
${ORCHESTRA_STATE_DIR:-/var/lib/orchestra/state}/monitor-metrics-prev.json | monitor | Previous metrics snapshot for delta-based alerting — stores total_requests, error_count, and latency counters from the last run; used to detect degradation (elevated error rates, latency spikes) between scrapes |
agents/<agent>/artifacts/<plan_id>.md | documenter, memoriser, monitor, maintainer, reviewer, auditor | Per-session markdown summaries written by scheduled and event-driven agents; surfaced in the TUI Artifacts tab |
Plan JSON shape
Written by the delegator into mas_tasks.plan_json. Validated by the dispatcher via Zod (planSchema.ts).
Standard plan (delegator-created):
json
{
"schema_version": "1",
"task_id": "STU-806",
"source": "linear",
"source_event_id": "...",
"created_at": "...",
"context": {},
"workers": [{
"id": "w1",
"agent": "developer",
"repo": "example-docgen",
"branch": "agent/developer/STU-806",
"task_input": {}
}]
}Handoff plan (dispatcher-created, source=handoff):
json
{
"schema_version": "1",
"task_id": "hoff-a1b2c3d4",
"source": "handoff",
"created_at": "2026-06-15T12:00:00.000Z",
"context": {
"handoff_from_agent": "monitor",
"handoff_from_task_id": "cron-monitor-20260615T080000Z-12345"
},
"workers": [{
"id": "w1",
"agent": "builder",
"repo": "orchestra",
"branch": "agent/builder/hoff-a1b2c3d4",
"task_input": {"summary": "...", "details": "..."}
}]
}Validation rules enforced by the dispatcher:
branchmust equalagent/{agent}/{task_id}repomust match[A-Za-z0-9._-]+(no slashes)schema_versionmust be"1"workersmin length 1
Indexes
idx_mas_tasks_statusonmas_tasks(status)— for dispatcher poll queriesidx_mas_tasks_sourceonmas_tasks(source)— for TUI Maintenance panel filtering by source typeidx_human_tasks_statusonhuman_tasks(status)idx_human_tasks_mas_taskonhuman_tasks(mas_task_id)idx_seen_events_received_atonseen_events(received_at)idx_running_workers_pidonrunning_workers(pid)idx_running_workers_statusonrunning_workers(status)idx_running_workers_agentonrunning_workers(agent)— optimizes overlap checks inrun-scheduled-agent.shthat query for active workers by agent name
TUI database queries
The TUI reads directly from the SQLite database for display purposes (read-only):
latestScheduledPerAgent()— For each unique scheduled agent, returns the most recentmas_tasksrow (byupdated_at) so the Scheduled panel can display last-run status (done/skipped/failed/running) inline with each timer.openMonitorTaskCount()— Counts openhuman_tasksfor the monitor agent. Used by the Scheduled panel's health badge: 0 → "Healthy", >0 → alert count.
Task lifecycle
pending (listener writes placeholder → delegator fills in plan via UPDATE)
└─ dispatcher picks up → validates plan
├─ invalid → failed
└─ valid → running
└─ all workers exit 0, no open human_tasks → done
└─ any worker exit non-0 → failed
└─ worker opened human_task → waiting_for_human
├─ human answers, no handoff → running (resumed) → done | failed
└─ human answers, handoff present → new pending task (source=handoff) for target agent → delegating worker marked done
Re-dispatch of same source_event_id: failed | done → revived to pending
(dedup entry cleared; delegator re-spawned with fresh context)