Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

orchestrator

An agent-and-skills orchestration system built only from Markdown instructions, a SQLite database, and the host's native subagent primitive. No application framework. No runtime service. No harness adapter. No Git plumbing.

This repository is the first build of the design described in the implementation plan handed off under the same name.


The problem

Most AI agent systems collapse under their own weight when work gets long, risky, or reviewable. The same failure modes keep showing up:

  • State lives in the conversation. When the context window fills, or the process restarts, you lose track of what was done.
  • Effects happen without audit. File edits, API calls, message sends happen ad hoc. There is no durable record of what changed, who decided it, or whether it actually landed.
  • The implementer is the verifier. The agent that wrote the patch is usually also the agent that says the patch is good.
  • There is no retry budget. Loops either run forever or stop arbitrarily. Neither is acceptable for serious work.
  • There is no escalation story. When the agent is stuck, the only option is "try again" — there is no structured way to ask a human, request a stronger model, or narrow scope.
  • There is no separation of powers. The agent that investigates also writes files, also approves decisions, also marks tasks done. There is no way to constrain what a sub-task is allowed to do.
  • Vendor lock-in. Most orchestrators wrap a specific AI harness, a specific runtime, a specific model API. Switching any of them means rewriting the orchestrator.

This system is an attempt to solve those failure modes with the smallest possible substrate.


What this is

A coordinator agent owns the outcome. It delegates bounded reasoning to host-native subagents through a small, fixed set of skills. The durable state — tasks, attempts, escalations, decisions, effects, audit events — lives in a SQLite database. The only piece of deterministic logic is the SQL itself: schema, transition tables, triggers, and canonical transaction recipes.

Operator
   │
   ▼
Coordinator agent              ← agents/coordinator.md
   │   owns the task, chooses specialists, delegates,
   │   interprets results, retries, escalates,
   │   applies effects, communicates with the operator
   │
   ├── orchestration skills    ← skills/orchestrate-work/SKILL.md,
   │                            plus nine narrower skills
   │
   ├── SQLite state ledger     ← schema/ + queries/
   │
   ├── host-native subagents   ← agents/investigator.md,
   │                            agents/planner.md,
   │                            agents/implementer.md,
   │                            agents/reviewer.md
   │
   └── coordinator tools       ← file edit, shell, messaging,
                                 ticket APIs

The portable part of the system is everything in this repository. The non-portable part — how the host invokes a subagent — stays host-native. The boundary is the schema and the agent and skill catalogues.

The repository makes a sharp distinction between what an agent is and what a procedure does:

  • agents/<name>.md declares an agent's identity, system prompt, tool binding, capability set, required skill, output contract.
  • skills/<name>/SKILL.md is the procedure the agent follows.

The host wires them together. Adding a new agent type means one file in agents/ and one in skills/. Adding a new procedure means one skill file and an entry in the relevant agent's "required reading" section.


What this is not

These are not implementation gaps — they are design choices. The implementation plan forbids each of these.

  • No orchestration application — no Go / Python / TypeScript / Rust service sitting between the coordinator and SQLite.
  • No harness adapter — the coordinator calls the host's native subagent primitive directly.
  • No MCP orchestration server — no extra protocol layer.
  • No Git, no branches, no worktrees, no commits, no merges — the implementer returns a patch as data; the coordinator applies it to the current file.
  • No per-agent workspaces, leases, or fence tokens — there is one coordinator, one database, one filesystem.
  • No automatic retry of uncertain effects — an effect that the coordinator cannot verify stays in reconciliation-required until a human decides.
  • No overclaiming model use — a request for a stronger model is recorded as such; only what the host actually used is recorded as observed.
  • No auto-promotion of skills or agents — promotion to a shared skill or a promoted dynamic agent is operator-authorised. The coordinator can propose via queries/propose-skill.sql or queries/propose-agent.sql, which opens a decisions row; the operator decides; the coordinator then runs the matching promote or reject recipe. The promotion recipes refuse to run without the operator's decision.
  • No dynamic principals — the coordinator can propose new bounded-subagent archetypes at runtime (recorded in the agents table, promoted only with operator authority), but it can never create a new coordinator or any principal, and dynamic agents are never user-invocable. Static agents in agents/<name>.md remain the source of truth for the four built-in archetypes.

The hard invariants

These are the rules the schema enforces and the skills protect:

  1. Only the coordinator writes SQLite.
  2. Only the coordinator applies external effects.
  3. A subagent may request escalation; only the coordinator decides it.
  4. A subagent may request capabilities; only the coordinator grants them.
  5. Granted capabilities are the intersection of root policy, task policy, and the specialist's request — not whatever the subagent asks for.
  6. Every subagent invocation has a durable attempt record.
  7. Every retry creates a new attempt. Terminal attempts are immutable.
  8. Every task mutation uses an expected version. Drift fails the recipe atomically.
  9. Reviewers never implement or repair. Failed review creates a separate implementer attempt.
  10. Task-local specialists do not mutate shared durable instructions. They are pinned to a task id and version, with skill versions and content hashes.
  11. Destructive effects and shared-skill/agent promotion require operator authority.
  12. The database alone is sufficient to reconstruct orchestration state after restart.

Quickstart

Nothing to install. You need sqlite3 (system version is fine) and a directory to drop a database file.

git clone <repo-url> orchestrator
cd orchestrator

DB=/tmp/orch_demo.db
rm -f "$DB"

# 1. Build the database from the canonical schema.
for f in schema/*.sql; do sqlite3 "$DB" < "$f"; done

# 2. Run the end-to-end success path (Section 26, success demo).
sqlite3 "$DB" < tests/e2e-success.sql

# 3. Inspect the outcome.
sqlite3 "$DB" "SELECT task_id, status, version FROM tasks;"
# task-success | completed | 7

sqlite3 "$DB" "SELECT attempt_id, status FROM attempts;"
# attempt-1        | succeeded
# attempt-reviewer | succeeded

sqlite3 "$DB" "SELECT effect_id, status FROM effects;"
# effect-1 | observed

sqlite3 "$DB" "SELECT COUNT(*) FROM events;"
# 26

That is the system working end-to-end: a task was created, an implementer attempt ran, an effect was proposed / approved / prepared / applied / observed, an independent reviewer attempt ran, and the task completed. Twenty-six events are recorded in the audit trail.

Try the failure path

DB=/tmp/orch_fail.db
rm -f "$DB"
for f in schema/*.sql; do sqlite3 "$DB" < "$f"; done
sqlite3 "$DB" < tests/e2e-failure.sql

sqlite3 "$DB" "SELECT task_id, status FROM tasks WHERE task_id='task-fail';"
# task-fail | failed

sqlite3 "$DB" "SELECT attempt_id, failure_code FROM attempts WHERE task_id='task-fail';"
# attempt-fail-1 | model-policy-violation
# attempt-fail-2 | unsupported-by-host
# attempt-fail-3 | unsupported-by-host

sqlite3 "$DB" "SELECT status FROM decisions WHERE task_id='task-fail';"
# resolved

The child asked for a frontier model. The task ceiling was strong-reasoning. The coordinator rejected the escalation, retried, hit the host's unsupported-by-host ceiling, exhausted the attempt budget, surfaced a decision to the operator, and the operator answered. Three failed attempts, no automatic retry past the budget.

Try the recovery path

DB=/tmp/orch_recover.db
rm -f "$DB"
for f in schema/*.sql; do sqlite3 "$DB" < "$f"; done
sqlite3 "$DB" < tests/e2e-recovery.sql

sqlite3 "$DB" "SELECT session_id, status FROM coordinator_sessions;"
# session-1 | interrupted
# session-2 | active

sqlite3 "$DB" "SELECT effect_id, status FROM effects WHERE effect_id='eff-rec';"
# eff-rec | observed

A coordinator started, applied an effect halfway (left it in applying), and "crashed". The next coordinator session marked the old session interrupted, moved the effect to reconciliation-required, and the operator resolved it. No automatic retry of the uncertain effect.


What's inside

orchestrator/
├── README.md                  this file
│
├── agents/                    agent definitions (5 files)
│   ├── coordinator.md         system prompt + tools + caps + entry skill
│   ├── investigator.md        read-only evidence gathering
│   ├── planner.md             ordered approach + conceptual effects
│   ├── implementer.md         effect proposals as data
│   └── reviewer.md            independent verification only
│
├── schema/                    canonical SQLite schema (8 files)
│   ├── 001_initial.sql            tables + indexes
│   ├── 002_views.sql              read-only projections
│   ├── 003_triggers.sql           transition + immutability +
│   │                              audit triggers
│   ├── 004_seed_transitions.sql   allowed_*_transitions rows
│   ├── 005_recipe_guards.sql      recipe_check sentinel
│   ├── 006_dynamic_skills.sql     skills table + transition
│   │                              guards for propose/promote/reject
│   ├── 007_denial_reason.sql      per-capability denial reasons
│   └── 008_dynamic_agents.sql     agents table + transition
│                                  guards for propose/promote/reject
│
├── queries/                   41 transaction recipes
│   ├── bootstrap-root-policy.sql   start here
│   ├── start-coordinator-session.sql
│   ├── create-task.sql
│   ├── start-attempt.sql            capability intersection
│   ├── submit-attempt-result.sql    terminal transition
│   ├── register / approve /
│   │   prepare / record-effect-*    effect lifecycle
│   ├── propose-skill.sql            coordinator drafts a skill
│   ├── promote-skill.sql            operator-authorised promotion
│   ├── reject-skill.sql             operator-authorised rejection
│   ├── propose-agent.sql            coordinator drafts a bounded agent
│   ├── promote-agent.sql            operator-authorised promotion
│   ├── reject-agent.sql             operator-authorised rejection
│   ├── complete-task.sql
│   ├── task-history.sql             reconstruct chronology
│   └── ...                          see file listing
│
├── schemas/                   JSON Schemas (6 files)
│   ├── task-contract.schema.json
│   ├── specialist-spec.schema.json
│   ├── attempt-result.schema.json
│   ├── escalation-request.schema.json
│   ├── effect-proposal.schema.json
│   └── reviewer-result.schema.json
│
├── skills/                    Markdown procedures (14 files)
│   ├── orchestrate-work/   coordinator entry point
│   ├── contract-task/      produce a task contract
│   ├── decompose-task/     split into child contracts
│   ├── select-specialist/  build a task-local specialist
│   ├── delegate-agent/     invoke a subagent
│   ├── evaluate-result/    independent checks on a result
│   ├── route-outcome/      pick the next durable action
│   ├── apply-effect/       coordinator-only effect lifecycle
│   ├── verify-result/      spawn an independent reviewer
│   ├── reconcile-state/    startup recovery
│   ├── investigator/       archetype
│   ├── planner/            archetype
│   ├── implementer/        archetype
│   └── reviewer/           archetype
│
├── docs/                      longer-form documentation
│   ├── architecture.md
│   ├── state-model.md
│   ├── operating-guide.md
│   └── limitations.md
│
├── evals/                     eval catalogue
│   ├── scenarios.yaml      25 scenarios from the plan
│   ├── scoring.md          two-track scoring rubric
│   ├── fixtures/           sample contracts, specs, results
│   └── expected/           recorded verdicts
│
└── tests/                     end-to-end SQL exercises
    ├── e2e-success.sql     Section 26 success demo
    ├── e2e-failure.sql     Section 26 failure demo
    ├── e2e-recovery.sql    restart + reconciliation demo
    ├── e2e-skill-proposal.sql  skill propose / promote / reject flow
    ├── e2e-agent-proposal.sql  agent propose / promote / reject flow
    └── README.md

What an operator sees

When the coordinator is running, every step is visible because every step is a row. Here is roughly what a session looks like, stripped of detail:

session-1        opened              2026-07-21T15:00:01Z
task-success     created             contract: objective + criteria
task-success     active              bound to attempt-1
attempt-1        started             model=standard, caps=read+propose+submit
escalation-1     requested           type=stronger-model, model=strong-reasoning
escalation-1     approved            reason: within ceiling, evidence supports
attempt-1        succeeded           observed_model=host-model-standard-v1
effect-1         proposed            file-change, risk=low
effect-1         approved            by coordinator
effect-1         applying            prepared
effect-1         applied             via coordinator mutation tool
effect-1         observed            after_sha256 captured
attempt-reviewer started             model=strong-reasoning (escalated)
attempt-reviewer succeeded           verdict=pass
task-success     completed           via coordinator
session-1        closed              no work remaining

The same chronology is recoverable from a closed database with one query: task-history.sql.


How a host uses this

A host needs three things:

  1. The ability to instantiate the coordinator agent defined at agents/coordinator.md (system prompt, tools, capabilities).
  2. The ability to instantiate archetype agents (agents/, investigator.md, planner.md, implementer.md, reviewer.md) when the coordinator delegates.
  3. A SQLite execution capability (the system's sqlite3 CLI, an MCP SQL tool, or any equivalent).

The coordinator follows skills/orchestrate-work/SKILL.md and composes the other nine coordinator skills. Subagents are invoked through skills/delegate-agent/SKILL.md with a specialist spec produced by skills/select-specialist/SKILL.md and a result contract enforced by schemas/attempt-result.schema.json.

The boundary is sharp:

  • agents/<name>.md says what the agent is.
  • skills/<name>/SKILL.md says what the agent does.
  • queries/*.sql says how state changes.
  • schemas/*.json says what the contracts look like.

There is no wrapper. The host reads these files and instantiates accordingly.


Asking the coordinator for a new skill

Yes — you can ask the coordinator for a new skill in conversation. The coordinator holds the skill.propose and skill.promote capabilities, but promotion is operator-authorised. The flow:

  1. You say: "create a skill for postgres migrations."
  2. The coordinator drafts the Markdown body, computes its sha256, and runs queries/propose-skill.sql. A row appears in skills with status proposed, and a decisions row of type approve-skill-promotion opens. The decision's request_json carries the full content for you to review.
  3. You review the content (read it from the database or via the coordinator's view of it). You answer the decision: {"decision": "promote", "reason": "..."} or {"decision": "reject", "reason": "..."}.
  4. The coordinator runs queries/promote-skill.sql or queries/reject-skill.sql. The recipe refuses to run unless the linked decision resolved with the matching choice — so the coordinator cannot promote skills without you.
  5. A promoted skill is part of the durable record. Its content_hash is what future specialist specs reference. To revise, the coordinator proposes a new version.

Skills can be shared or task-local. A shared skill is available to any task. A task-local skill is bound to a specific task_id and only usable for that task.

Asking the coordinator for a new agent

Yes — with the same operator-authorised flow as skills. The coordinator holds the agent.propose and agent.promote capabilities, and promotion is operator-authorised:

  1. You say: "create a specialist for SQL migrations."
  2. The coordinator drafts the agent content (same shape as agents/<name>.md: identity, system prompt, tool binding, capability ceiling, output contract), picks the base_archetype whose accountability profile it inherits, computes the sha256, and runs queries/propose-agent.sql. A row appears in agents with status proposed, and a decisions row of type approve-agent-promotion opens.
  3. You review the content and answer the decision: {"decision": "promote", "reason": "..."} or {"decision": "reject", "reason": "..."}.
  4. The coordinator runs queries/promote-agent.sql or queries/reject-agent.sql. The recipes refuse to run without your decision — the coordinator cannot promote agents alone.

Hard constraints, enforced at the data layer:

  • A dynamic agent is always a bounded-subagent — never a coordinator, never a principal, never user-invocable. It is reachable only through a specialist spec on an attempt.
  • Specialist specs pin it by name, version, content_hash; select-specialist fails on any row that is not promoted.
  • Its base_archetype (investigator, planner, implementer, or reviewer) drives the capability subtractions — a dynamic agent can never hold effect.apply, task.complete, agent.delegate, skill.promote, or agent.promote.
  • Promoted agents are immutable; revisions are new versions.

No host restart is needed: the coordinator builds every child prompt dynamically at delegation time, so a promoted agent is just prompt content read from SQLite instead of disk. Static agents in agents/<name>.md remain the source of truth for the four built-in archetypes; adding a new built-in archetype is still an operator-write-then-restart operation.


Reading order if you want to understand the design

  1. README.md — the binding commitments.
  2. agents/coordinator.md — what the coordinator agent is.
  3. docs/architecture.md — the four components and why there is no fifth.
  4. docs/state-model.md — every table, every transition, every trigger.
  5. docs/operating-guide.md — what the coordinator does, step by step.
  6. docs/limitations.md — what the system deliberately does not guarantee.

For a quick read, README.md and docs/architecture.md are enough. For implementation work, start with docs/state-model.md and queries/.


Status

First build. The required first demonstrations (success path, failure path, recovery path from Section 26) all pass end-to-end against sqlite3 3.51.0. The 25 eval scenarios are catalogued in evals/scenarios.yaml; the DB-track portion is verified by the e2e tests, the host-track portion is documented for hosts to implement.

No broader behaviour should be added until the host-side integration is exercised on a real subagent primitive.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors