Personal agent swarm infrastructure. Every agent action attributed, versioned, and queryable.
Built around Neotoma as the canonical memory and state layer. T1 hosts, T2 resident agents, T3 daemons, and T4 invocable agents — all defined as Neotoma entities, dispatched through AAuth-signed identities, and audited through the same observation log they write to. Open source. Local-first. MIT licensed.
Architecture: docs/architecture.md · Taxonomy: docs/taxonomy.md · Phases: docs/phases.md
You run AI agents across tools and tasks. Without a swarm infrastructure layer, you become the swarm:
- Every agent operates from zero context — nothing it learns is shared across agents or sessions
- Identities collapse — a code-writing agent acts as you on GitHub, and there's no audit trail tying the AAuth subject to the action
- Decisions execute without a reproducible trail — you can't trace why an agent did something or whether it stayed in scope
- Orchestration is ad-hoc — kicking off a multi-agent workflow means manually opening multiple chats and hoping they don't conflict
These are not hypothetical. They are what happens when a single operator runs more than three agents in parallel across more than two products. You compensate with bespoke scripts, redundant prompts, and manual sync. Ateles removes that tax.
Ateles is a reference architecture for a single-operator agent swarm where:
- Agents are entities, not files. Each agent_definition lives in Neotoma. Updating an agent's behaviour is a
correct()call, not a code commit. SKILL.md files on disk are generated mirror artifacts. - Every action is attributed. Agents authenticate via AAuth-signed JWTs. The
github_harnessMCP server records every tool call as anagent_action_observationwith both the AAuth subject (who claimed to act) and the PAT attribution (who actually acted on GitHub). - Workflows are typed. A
workflow_definitionentity declares phases, gates, owner agents, and skip conditions. Anthus (the swarm coordinator) reads workflows from Neotoma and dispatches gates in order. - State is canonical. Participation records, release criteria, agent grants, and policies are all Neotoma entities. The swarm has no other source of truth.
Not a framework. Not a toy. A working production blueprint that runs daily under launchd.
graph TB
Operator[Operator markmhendrickson] -->|Telegram, terminal| Onychomys
Onychomys[Onychomys T2 — primary interface] -->|paged via priority_rubric| Operator
Anthus[Anthus T3 — swarm coordinator] -->|notifies| Onychomys
Anthus -->|dispatch| Gryllus[Gryllus T4 — code]
Anthus -->|dispatch| Vanellus[Vanellus T4 — review]
Anthus -->|dispatch| Pavo[Pavo T4 — PM]
Anthus -->|dispatch| Others[20+ other T4 agents]
Gryllus & Vanellus & Pavo & Others -->|every tool call| Harness[github_harness MCP]
Harness -->|AAuth verify, PAT lookup, observation record| Neotoma[(Neotoma)]
Apus[Apus T3 — mirror webhook receiver] -->|regenerates SKILL.md from| Neotoma
Neotoma -->|SSE events| Anthus
Neotoma -->|SSE events| Formica[Formica T3 — issue triage]
Neotoma -->|SSE events| Other_Daemons[Piculet · Strix · Turdus · Monedula]
- AAuth-verified. Every agent has a keypair. Every tool call is signed. The harness verifies before acting.
- Capability-scoped.
agent_grantentities declare which agents can call which tools against which repos. Wrong-capability calls fail with structured errors. - Observation-backed. Every harness call writes an
agent_action_observation. Replay any agent's actions over any time window from the log. - Mirror-canonical. Agent definitions live in Neotoma; Apus mirrors them to disk as SKILL.md files. Direct disk edits are reverted on next mirror pass.
| Foundation | What it means |
|---|---|
| Attributed | Every action records agent_sub (AAuth identity) and pat_attribution (GitHub identity). Trust boundary visible at every call. |
| Capability-scoped | Agents only have the powers their agent_grant declares. Out-of-scope calls return wrong_capability before any side effect. |
| Workflow-driven | Phases, gates, and skip conditions live in workflow_definition entities. Anthus reads them and dispatches accordingly — no hardcoded sequencing. |
| Canonical-source-of-truth | Agent definitions, grants, policies, and participation records all live in Neotoma. Disk artifacts are mirror outputs, never inputs. |
Full design: docs/architecture.md.
Everything Ateles knows about itself is a Neotoma entity. The filesystem is a generated mirror — useful for IDE tooling, but never the source of truth.
| Concept | Lives as | Mirrored to | Direction |
|---|---|---|---|
| Agent prompt | agent_definition |
.claude/skills/<agent>/SKILL.md |
Neotoma → disk |
| Agent capability | agent_grant |
(none — read live by github_harness) | Neotoma only |
| Workflow phases | workflow_definition |
(none — read live by Anthus) | Neotoma only |
| Gate state | participation_record |
(none — written by harness) | Neotoma only |
| Every tool call | agent_action_observation |
(none — append-only log) | Neotoma only |
| Operating rules | agent_policy |
(none — enforced at dispatch) | Neotoma only |
| Strategy posture | business_strategy / domain_strategy / agent_strategy |
(none) | Neotoma only |
What this means in practice:
- Behaviour changes are corrections, not commits. Updating Pavo's prompt is
neotoma correct --entity-id ent_… --field prompt_markdown. Apus regenerates the SKILL.md mirror on the next webhook. Direct edits to.claude/skills/pavo/SKILL.mdare reverted. - Capability changes are entities, not configs. Granting Gryllus access to a new repo is a
correct()on theagent_grantentity. The nextgithub_harnesscall sees the new scope on the next pre-check; no daemon restart. - Workflow changes are entities, not code. Adding a phase to the copy workflow is a
correct()on theworkflow_definitionentity. Anthus picks it up from the SSE event stream. - Audit is a query, not a grep. "What did Gryllus do last Tuesday?" is
retrieve_entities --entity-type agent_action_observation --agent-sub gryllus@ateles-swarm. Git logs only show committed code; observations cover every harness call, including reads. - Reasoning about the swarm is reasoning about entities. The mental model is "which entities flow through which daemons", not "which files do which scripts read".
The filesystem layout matters for the runtime substrate — daemons need code on disk, Claude Code needs SKILL.md on disk — but it is downstream of the entity model, not the other way around.
Four tiers. Twelve product-panel agents plus daemons plus 20+ domain T4s. Naming follows bird and animal genera.
| Tier | Role | Examples |
|---|---|---|
| T1 | Hosts (process that owns a channel) | OpenClaw (Telegram), launchd (daemon scheduling), raw aiohttp Bot API |
| T2 | Resident agents (always-on, conversational) | Onychomys (primary operator interface), Menura (public-facing personal representative) |
| T3 | Daemons (event-driven, persona-less) | Formica · Anthus · Apus · Apis · Piculet · Strix · Turdus · Monedula · neotoma-agent · Tyto · mic-recorder · Cyphorhinus |
| T4 | Invocable agents (stateless, spawned per task) | Pavo · Bombycilla · Phoenicurus · Buteo · Luscinia · Struthio · Accipiter · Corvus · Regulus · Gryllus · Vanellus · Paradisaea · and 20+ domain agents |
Full agent table with AAuth identities, grants, and status: docs/taxonomy.md.
Ateles is not an installable package — it's a reference architecture. The fastest way to evaluate whether to adopt the pattern is to read the architecture doc and inspect a working daemon.
git clone https://github.com/markmhendrickson/ateles.git
cd ateles
less docs/architecture.md
less execution/daemons/anthus/anthus.py
less execution/daemons/apus/apus.pyTo run any daemon locally requires Neotoma running (see neotoma installation), AAuth keypairs provisioned, and agent_grant entities for each daemon. The full setup walkthrough lives in docs/setup.md.
Three umbrella issues shape what adoption looks like next:
- ateles#18 — Make Ateles installable — ten blockers to package-based adoption (identity provisioning UX, operator-specific agent definitions, config schema, keypair format unification, launchd plist generation, dependency manifest,
ateles-private/boundary, versioning, multi-operator path). Adopt-by-forking works today; install-by-package needs this work. - ateles#19 — Input attribution — record which Neotoma entities each agent read before deciding. Today the swarm records who acted and what they produced, but not the evidence the decision was built on. Closing this loop enables reverse impact analysis (which downstream decisions does a corrected strategy invalidate?) and precedent graphs. Broken into execution sub-issues #20–#25.
- ateles#26 — Tool-level authorization — extend
agent_grantto declare which MCP tools each agent can call, against which resources, with which parameter constraints (e.g. Monedula can callbtc_send_transferup to 500k sats; Pavo cannot callbtc-walletat all).github_harnessalready implements this pattern; #26 generalizes it across all MCP servers via a proxy enforcement layer.
A real Pavo dispatch traced through the entity layer. The CLI invocation is one step among ten; the rest happens in Neotoma.
-
Operator files an issue.
gh issue create --repo markmhendrickson/ateles --title "Decide feature A vs B for next release". The issue body is the task body for Pavo. -
formica(T3) ingests the issue. It reads the GitHub webhook event, callsstore(issue, ...)on Neotoma, and emits a triage observation classifying it as a product decision. -
anthus(T3) reads the SSE event stream. It matches the newissueentity againstworkflow_definitionentities and picks theproduct_decision_workflow. -
Anthus opens a
participation_record. One per gate. The first gate hasowner_agent: pavo,status: dispatched,dispatched_at: now(). -
Anthus reads
agent_definitionfor Pavo from Neotoma. It pullsprompt_markdown,context_entity_types,operational_entity_types, andallowed_tools. The SKILL.md mirror is the same content on disk — Anthus could use either, but Neotoma is canonical. -
Anthus spawns a
claude --printsubprocess with Pavo's SKILL.md as--append-system-promptand the issue body as the user prompt. Pavo's AAuth keypair is loaded fromateles-private/keys/. -
Pavo reads context entities through
mcpsrv_neotoma. It queries recentbusiness_strategy,domain_strategy, customer-signal observations, prioracceptance_criteriaentities — whatever itscontext_entity_typeslists. -
Pavo writes its decision. It calls
store(acceptance_criteria, body="Build feature B first…")on Neotoma. The observation is attributed topavo@ateles-swarmvia AAuth signature. Pavo also emits the artifact-header line as its final stdout:[pavo] acceptance_criteria: Build feature B first. Higher strategic fit, lower implementation risk after webhook v0.1, customer-signal weight 2.3× feature A. -
Anthus parses the artifact header, updates the
participation_recordtostatus: satisfied,satisfied_at: now(),artifact_ref: ent_…pointing at the newacceptance_criteriaentity. -
Anthus advances the workflow. It opens the next
participation_recordfor the next gate (e.g.gryllusto scaffold the implementation), and the loop repeats.
Everything Anthus does after step 1 is driven by entities. The operator can replay the full sequence later with retrieve_entities against agent_action_observation and participation_record. No grep, no git log.
The filesystem is the runtime substrate that supports the entity layer. This is the mirror, not the source — every behaviour-defining artifact below is generated from or read by Neotoma entities at runtime.
ateles/
├── .claude/skills/ # generated SKILL.md mirrors (do not edit — corrections go through Neotoma)
├── docs/
│ ├── architecture.md # system design and Neotoma integration
│ ├── taxonomy.md # canonical agent table
│ ├── phases.md # implementation roadmap (mirror of Neotoma plan)
│ ├── swarm_orchestration.md # workflow_definition + gate semantics
│ ├── swarm_smoke_test_plan.md # tiered smoke-test programme
│ └── setup.md # daemon installation + AAuth provisioning
├── execution/
│ ├── daemons/ # T3 daemon implementations
│ │ ├── anthus/ # swarm coordinator — workflow dispatch
│ │ ├── apus/ # mirror webhook receiver — Neotoma → git pipeline
│ │ ├── apis/ # universal task dispatcher
│ │ ├── formica/ # GitHub issue/PR triage
│ │ ├── monedula/ # recurring payment daemon (Wise + BTC)
│ │ ├── neotoma-agent/ # neotoma-repo automation
│ │ ├── piculet/ # audio transcription watcher
│ │ ├── strix/ # meeting/ambient audio recorder
│ │ ├── turdus/ # email triage daemon
│ │ ├── tyto/ # screenshot watcher
│ │ ├── mic-recorder/ # menu bar mic toggle + launchd
│ │ └── cyphorhinus/ # recurring report generator
│ ├── scripts/ # domain scripts (transcription, finance, OCR, etc.)
│ └── mcp-servers/ # local MCP server packages
└── lib/
├── daemon_runtime/ # SSE subscription, AAuth signer, agent_definition loader
└── notify/ # Apprise-backed notification routing
- Neotoma — canonical memory and state layer
- mcp-server-github-harness — AAuth-verified GitHub MCP with PAT loader and observation recording
- Claude Agent SDK / Claude CLI — agent execution runtime
- Apprise — notification delivery (Telegram, email, etc.)
gwsCLI — Google Calendar / Gmail integrationneotomaCLI — Neotoma entity operationslaunchd— macOS scheduling for T3 daemons
The swarm makes specific commitments about what is verifiable.
| Property | Without swarm infra | Ad-hoc agents | Ateles |
|---|---|---|---|
| 🪪 Agent identity verifiable | ❌ | ✅ AAuth-signed JWT | |
| 🔍 Trust boundary visible (sub ≠ pat) | ❌ | ❌ | ✅ both recorded |
| 🚧 Capability scope enforced | ❌ | ✅ agent_grant pre-check | |
| 📋 Per-action audit trail | ❌ | ✅ agent_action_observation | |
| 🔄 Workflow phase ordering | ✅ workflow_definition + Anthus | ||
| ⏭️ Gate skip conditions | ❌ | ❌ | ✅ declared per-workflow |
| ⏪ Replayable orchestration | ❌ | ❌ | ✅ participation_record log |
| 📜 Agent definitions are versioned | ❌ | ✅ Neotoma observation history | |
| 🔔 Operator paged only on real escalations | ❌ | ✅ priority_rubric filter | |
| 🧠 Inputs to a decision are recorded | ❌ | ❌ | |
| 🔗 Agent version pinned at dispatch | ❌ | ||
| 🪜 Precedent chain queryable | ❌ | ❌ |
The first nine rows are live in operator-driven use today. The final three rows are tracked in ateles#19 — input attribution, which records the Neotoma entities each agent consulted before deciding. Output attribution (who acted, against which capability, producing what) is solved; input attribution closes the loop so decisions can be audited against the evidence they were built on.
The swarm exposes four operational surfaces, all converging on Neotoma as source of truth.
| Interface | Description |
|---|---|
| Telegram (Onychomys) | Primary operator surface. Bot at OnychomysBot. Receives BLOCKER / OPERATOR_DECISION pages; the operator replies inline. |
claude --print |
Agent dispatch surface used by both operator and Anthus. Each invocation passes --append-system-prompt "$(cat SKILL.md)". |
| GitHub harness MCP | Every code-touching agent's call surface. AAuth-verified, capability-scoped, observation-logged. |
| Neotoma SSE | The substrate. Daemons subscribe by entity type; new issues, PRs, escalations, daemon reports flow through this stream. |
Solo operators running a multi-agent personal stack — agents for code, content, finance, household ops, customer development, residency admin, and so on — who need to keep all of them coordinated, auditable, and within scope. Three modes, one operator:
| Mode | What you're doing | The tax without Ateles | What you get back |
|---|---|---|---|
| Operating | Dispatching agents across products and tasks | Manual sequencing, redundant context-setting, no idea who did what | Single dispatch surface; verifiable history |
| Building | Adding new agents or workflows | Hardcoded sequencing, ad-hoc identity glue, no contract between agents | Declarative workflow_definition; reusable grants |
| Debugging | Tracing why an agent acted out of scope | Reading git logs, guessing which agent ran when | observation replay over any time window |
Not for: Single-agent setups. Teams (Ateles assumes one operator across all agents — multi-operator requires multi-tenant Neotoma). Hosted-agent providers. Anyone who needs zero-install onboarding (Ateles is a reference architecture, not a packaged product).
Every swarm-defining record is a typed Neotoma entity. The canonical entity_type values that Ateles reads and writes:
entity_type |
What it stores | Key fields |
|---|---|---|
agent_definition |
Identity, prompt, triggers, context types, operational types, allowed tools | name, triggers, prompt_markdown, allowed_tools, context_entity_types, operational_entity_types |
agent_grant |
Capability scope — Neotoma entity-type allowlist + MCP tool allowlist + external resource scope | agent_sub, op, repos, capabilities (entity ops + tools — ateles#26) |
agent_action_observation |
One row per harness tool call — AAuth sub, PAT attribution, tool, owner/repo, result | agent_sub, pat_attribution, tool, owner, repo, result |
agent_policy |
Operating rules (mandatory/recommended/prohibited) with scope | scope, rule_kind, body |
workflow_definition |
Phases, gates, owner agents, skip conditions, preconditions | name, gates[], gate_name, owner_agent, skip_if, preconditions |
participation_record |
Per-gate dispatch + satisfaction history | workflow_id, gate_name, status, dispatched_at, satisfied_at, artifact_ref |
business_strategy / domain_strategy / agent_strategy |
Hierarchical strategy DAG — root + per-area + per-agent posture | north_star_metric, assumptions, success_criteria, parent_strategy_ref |
strategy_drift_signal |
Agent-emitted observation when work contradicts current operating assumptions | observation, severity, relates_to_assumption |
Full type catalog: docs/data_types.md.
Phase: Reference architecture in operator-driven use. Operator: one (Mark Hendrickson). Daemons running under launchd: Apus, Formica, Anthus. License: MIT.
- Agent definitions in Neotoma → mirrored to disk (Apus pipeline)
- AAuth verification + capability scoping (
github_harnessMCP, withagent_grantlookup) - Anthus dispatch via
claude --print --append-system-prompt(workflow_definition-driven, fire-and-forget subprocess) participation_recordandagent_action_observationschemas registered and queryable- 23-schema slate for strategy hierarchy, doc surfaces, code review, release criteria, brand voice, etc.
- Per-agent context + operational type + allowed_tools declarations populated for all 12 product-panel agents
- Operator-driven Tier 1 smoke test — single-agent gate satisfaction (12 agents, artifact-header convention)
- Tier 2 — phase ordering + parallelism on synthetic issues
- Tier 3 — multi-repo identity correctness (AAuth → PAT mapping)
- Tier 4 — real product work end-to-end
- Tier 5 — fully autonomous Anthus dispatch
- Stable workflow_definition schema across versions
- Anthus runs without intermittent restarts
- Schema sprawl is consolidated (feedback / UI / release types still overlap)
- Backward compatibility — corrections may invalidate prior gate outputs
See docs/swarm_smoke_test_plan.md for the full phased rollout cadence.
The runtime substrate that supports the entity layer. AAuth keypairs, PATs, and the Apus tunnel sit beneath Neotoma; the swarm's trust model and audit trail are defined by entities (agent_grant, agent_action_observation), not by the on-disk material that runs the daemons.
Ateles is single-operator infrastructure. Defaults assume the operator owns the machine, the keypairs, and the Neotoma instance.
- Authentication: AAuth keypairs per agent, signed JWTs verified against published JWKS. Operator token used as fallback for daemons without their own grant.
- Authorization:
agent_grantentities pre-check every tool call. Wrong-capability returns structuredwrong_capabilityerror before any side effect. - Sensitive material: GitHub PATs live in
.envonly, loaded per-repo by the harness PAT loader. Agents never see PATs. - Audit: Every harness call writes an
agent_action_observation. Replay over any time window. - Tunnel: Apus webhook receiver listens on
localhost:8741only; Cloudflare tunnel surfaces it to Neotoma's prod webhook deliveries.
Daemons run from disk under launchd; the IDE reads SKILL.md mirrors from disk. Both are runtime conveniences. The only artifact that defines swarm behaviour is an entity in Neotoma — every workflow below reflects that.
Daemon iteration:
# venv at .venv/ with httpx, apprise, etc.
.venv/bin/python3 execution/daemons/anthus/anthus.py # foreground test
launchctl unload ~/Library/LaunchAgents/com.ateles.anthus.plist
launchctl load ~/Library/LaunchAgents/com.ateles.anthus.plist
tail -f /tmp/com.ateles.anthus.err.logUpdating an agent's prompt (the canonical path):
# Never edit .claude/skills/<agent>/SKILL.md directly — Apus reverts on next mirror.
# Correct the agent_definition entity instead:
neotoma correct --entity-id ent_… --field prompt_markdown --value "$(cat new_prompt.md)"
# Apus webhook fires; SKILL.md regenerates from the canonical entity.Updating an agent's capability scope:
# agent_grant is also an entity. Add a repo to Gryllus's github_harness:write scope:
neotoma correct --entity-id <gryllus-grant-id> --field repos --append neotoma-docs
# Next harness call sees the new scope on the next pre-check; no daemon restart.Adding a workflow phase:
# workflow_definition is an entity. Insert a new gate:
neotoma correct --entity-id <workflow-id> --field gates --json '[...new gate...]'
# Anthus picks it up from the SSE event stream on the next dispatch.Smoke testing:
# Operator-driven Tier 1 — single-agent gate
claude --print --append-system-prompt "$(cat .claude/skills/pavo/SKILL.md)" \
--dangerously-skip-permissions \
"Invoke pavo. <task body>"Prerequisites: Python 3.13+ (.venv/), Node v22+ via NVM (for claude CLI), Neotoma running on prod URL, the op CLI for .env loading, gh and gws CLIs.
Ateles operates through claude CLI and MCP servers. The MCP layer is what connects each agent to the Neotoma state layer and the GitHub harness.
| Tool | How Ateles uses it |
|---|---|
| Claude Code | Operator-driven sessions in worktrees. Skills under .claude/skills/ invoke agents by trigger word. |
claude --print |
Headless agent dispatch (operator and Anthus). --append-system-prompt loads the agent's SKILL.md. |
| mcpsrv_neotoma | Every agent reads/writes Neotoma state via this MCP. Schema-aware tool surface. |
| github_harness MCP | AAuth-verified GitHub operations. Issues, PRs, comments, branches, commits, reviews — all observation-logged. |
| typefully MCP | Corvus's social-post scheduling surface. |
| parquet MCP | Bulk-data query surface for finance, transcription, and WhatsApp parquet stores. |
Agent dispatch contract: Agents emit a single bracketed artifact-header line as their final response ([<agent>] <artifact_kind>: <body> or BLOCKED — <reason>). Anthus parses this to mark workflow gates satisfied. Agents optionally append a second line, [<agent>] strategy_drift_signal: <observation>, when their work surfaces evidence contradicting their current agent_strategy. See docs/swarm_orchestration.md for the full contract.
Why a reference architecture instead of a packaged framework? A single-operator agent swarm is too tightly coupled to the operator's specific tooling, repos, identities, and grants to package generically. Ateles is the design and a working example — fork it, adapt the daemons to your infrastructure, keep the contract (AAuth identity, observation recording, workflow_definition, mirror pipeline).
How does this compare to LangGraph / CrewAI / AutoGen?
Those frameworks orchestrate multi-step LLM calls inside one process. Ateles orchestrates long-running background daemons that spawn claude subprocesses, each with its own AAuth identity, and that write to a canonical state store. Different problem — agent fleets across days, not chains across seconds.
Why launchd instead of Docker / k8s? Single operator, one machine. launchd is the lowest-overhead scheduler that survives reboots. The architecture would work the same way under systemd or supervisord; the daemons don't care.
Is this production-ready? Tier 1 (single-agent gate satisfaction) passes. Tier 5 (full autonomous Anthus dispatch) is still scaffolded. Daily real use: Apus, Formica, Anthus, Monedula run under launchd. Most T4 agents are operator-dispatched. See docs/swarm_smoke_test_plan.md for the gating tiers.
What if I want to add a new agent?
File an agent_definition entity in Neotoma. Mint an AAuth keypair. File an agent_grant with the capabilities it needs. Apus mirrors the SKILL.md to disk. Anthus picks it up on next workflow that references its trigger. The whole loop is documented in docs/swarm_orchestration.md.
- Your agent fleet now signs its writes — Neotoma v0.6 fleet-grade attribution
- Your agent's memory is a markdown file — convergent evolution of file-based memory
- Building a truth layer for persistent agent memory
- Agent command centers need one source of truth
- Six agentic trends I'm betting on
Full documentation lives in docs/.
Getting oriented: Architecture, Taxonomy, Phases
Operating: Swarm orchestration, Swarm smoke-test plan, Agent execution runbook, Setup
Reference: Data types, Auth + key management (see docs/), Data publishing transformation
Operations: Workflows, Usage, Linting guide, Test setup
Ateles is in active development by one operator. Issues and discussions welcome — open one at github.com/markmhendrickson/ateles/issues. Pull requests for the reference architecture (daemon hardening, AAuth improvements, harness extensions) are reviewed. Pull requests that bundle in operator-specific configuration are not — fork instead.
License: MIT