Moirai: Orchestrating Thin LLM Agents Without the Bespoke Wiring
How two nightly LLM agents became Moirai — a reusable orchestrator now running three fate-named agents (Clotho, Lachesis, Atropos), one of which is trusted to actually close things.
The first time you wire an LLM agent into a CI pipeline, it's a workflow file. The second time, it's a workflow file plus a config plus a prompt, almost the same shape as the first but you copy-paste and tweak. By the fourth or fifth, you have five subtly different setups, none of them improvements over the others, and any change to the agent's behaviour means editing five places.
That's roughly where I was last week. I had two LLM agents living inside monospace.studio: Curator, a nightly scanner that files GitHub issues for cleanup candidates, and Auditor, a PR-scoped reviewer that surfaces coherence problems into my session inbox. Both worked. Neither was reusable. The prompts, the config, the workflow wiring, the secrets handling, all entangled with this one repo.
So I split them. And I built the thing that lets them plug in cleanly.
What Moirai Is
Moirai (named for the Greek fates: Clotho spins the thread, Lachesis measures, Atropos cuts) is an orchestrator for thin LLM agents. It's the glue, not an agent host.
Three layers, separated cleanly:
| Layer | What it is | Examples |
|---|---|---|
| Agent repo | One agent, one purpose. AGENT.md (persona), PROMPT.md (instructions), schema.json (config shape), defaults.yml (sane defaults). No workflows, no config values, no host-specific wiring. | derrybirkett/clotho, derrybirkett/lachesis, derrybirkett/atropos |
| Moirai | Registry, protocol spec, host adapters, installer. The substrate that lets thin agents work the same way everywhere. | derrybirkett/moirai |
| Consuming repo | Submodules Moirai. Per agent, has .github/agents/<name>/config.yml (validated against the agent's schema) + a workflow per trigger. | monospace.studio, future adopters |
The core idea is composition over inheritance. Each agent is independent and versionable. A consuming repo mounts the ones it needs. When an agent improves (sharper prompt, better scope detection, smarter dedup), every consumer inherits the improvement on a single submodule bump.
The agent definition is the compounding asset. Per-repo tuning is local and disposable.
Installing an Agent
In the consuming repo:
git submodule add https://github.com/derrybirkett/moirai .moirai
.moirai/bin/install lachesis
That single install command does four things:
- Reads Moirai's
registry.ymlto find the agent and its pinned tag. - Submodules the agent at that tag under
.agents/lachesis/. - Scaffolds
.github/agents/lachesis/config.ymlfrom the agent'sdefaults.yml(the consumer overrides; unset fields fall back). - Renders
.github/workflows/lachesis.ymlfrom the chosen adapter's template, with permissions and tool access derived from the declared sink.
Edit the config to taste, commit four files, push. The agent runs on its trigger.
The Walking Skeleton
I built Moirai bottom-up. Top-down spec-first risks over-design: you describe a protocol that solves problems that don't exist. So I extracted one agent end-to-end, built the thinnest possible orchestrator around it, and only then started writing the spec.
Step 1: Curator. Lifted out of a submodule and into its own repo. Adapted the prompt to read its config from $AGENT_CONFIG_PATH (host-injected) instead of a hard-coded path. Replaced the literal config.yml with defaults.yml + a JSON Schema. Two days ago this was Moirai v0.1.0.
Step 2: orchestrator. Wrote protocol/SPEC.md, registry.yml, the bash installer, the GitHub Actions adapter with a parameterised workflow template.
Step 3: consume. Refactored monospace.studio to install Curator via Moirai. First real test of the installer. Found one bug immediately, git rejecting a submodule nested inside another submodule:
fatal: Pathspec .moirai/agents/curator is in submodule .moirai
Fixed (agents now live at .agents/<name>/, sibling to .moirai/). Curator filed five real GitHub issues that night.
Step 4: the moment of truth. Extracted Auditor as the second agent. This is the test of whether the protocol I wrote for Curator actually accommodates a different shape. Auditor's trigger is pull_request, not schedule. Its sink is inbox (writes to a markdown file the host commits back), not issues. Its envelope needs additional environment variables: PR_NUMBER, PR_BASE_SHA, PR_HEAD_SHA.
The protocol mostly held. The bits that needed adding (conditional template blocks, the inbox commit-back contract, a fork-safety skip) shipped cleanly as Moirai v0.2.0 without rewriting Curator's path. Auditor ran on its own PR and the auditor-bot pushed back the audit block. End-to-end validated across two agents with fundamentally different shapes.
That's the whole loop: build the thinnest possible version of the system, prove it works, then extract the next thing and let the friction tell you what to fix.
The Fate Transition
Curator and Auditor didn't last as their own agents. On 2026-06-05 (a week and a half after the walking skeleton proved out), both were retired and folded into Lachesis, one of the three fates the whole system was named after in the first place. One PR did the whole transition:
feat: Moirai Fate transition — install clotho + lachesis, retire curator + auditor (#124)
The commits that mattered: install Clotho, install Lachesis, then bring Lachesis to full parity and delete Curator's and Auditor's workflows and configs outright. A session handover closed it out, documenting the trio complete.
The reason wasn't cosmetic. Curator's dead-exports/oversized-files/component-drift scans and Auditor's link-integrity/orphans/copy-drift checks were both, underneath, the same shape of work: read the repo at HEAD, score what already exists, file an issue for what falls short. Two agents doing one job with two config schemas and two workflows to maintain. Lachesis merged them into one agent with three dimensions: quality (Curator's turf), coherence (Auditor's), plus a new coverage dimension (thin entries, missing required fields) that neither predecessor covered. A fourth dimension, stale_branches, followed later. Lachesis flags cold branches; Atropos deletes them once the grace window clears.
Clotho is genuinely new, not a merge of anything. She holds a line Lachesis doesn't: absence, not adequacy. A nextCase: "payfit" pointing at a payfit.mdx that was never written is Clotho's gap. The same file existing with an empty outcome field is Lachesis's thin entry. Two agents, one clean boundary: the moment an artifact exists, even as an empty stub, it leaves Clotho's hands and becomes Lachesis's to measure.
So the roster settled into the three fates the metaphor was always pointing at: Clotho spins (names what's missing), Lachesis measures (scores what exists), Atropos cuts (closes what's gone stale). Curator and Auditor stay in the registry, retired: true, kept for provenance: the walking skeleton that proved the protocol, superseded by the thing the protocol was actually for.
What You Get For Free
Beyond the running agent, Moirai installs a set of guarantees:
A typed sink interface. Every agent declares what it writes to: issues | inbox | summary | pr-comment. The installer derives the right GitHub Actions permissions from the sink. Clotho and Lachesis get issues: write (observe-trust, file and score); Atropos gets issues: write plus the extra reach an act-trust agent needs to close, warn, and delete. The consumer doesn't have to think about it.
The envelope. Every agent receives the same environment-variable contract: AGENT_NAME, AGENT_CONFIG_PATH, AGENT_REPO_DIR, TODAY. PR-triggered agents additionally receive PR_NUMBER, PR_BASE_SHA, PR_HEAD_SHA. Agents read their config from a path the host injects; they never assume a location.
Stable schemas for inter-agent reads. A future meta-agent (call it Conductor) can read across [clotho] <category>: and [lachesis] <dimension>: issue titles and propose cross-cutting action. Thin agents don't have to know about each other; the substrate enables emergence.
One-command install. Pinning a new agent in registry.yml is the entire "add to catalog" step. From the consumer's side, bin/install <name> is the entire "adopt" step.
Version-locked behaviour. Both agents and Moirai itself are tagged. A consumer pins .moirai and .agents/<name> to specific tags. Changes are opt-in via submodule bumps, never surprise.
What You Still Have To Build
Moirai is honest about its scope.
- The agents themselves. Moirai doesn't write prompts. Each agent is its own repo with its own prompt, scopes, and config schema. The agent's quality is entirely the agent's concern.
- Cross-LLM portability. Today there's exactly one adapter, for Claude Code via
anthropics/claude-code-action@v1on GitHub Actions. The architecture is designed so an OpenAI Assistants adapter or a local CLI runner could slot in without changing any agent. I haven't built one yet. - The meta-agent. The interop pattern is there in the protocol (typed sinks, stable schemas), but no agent currently reads across sinks. That's the next horizon.
Atropos, For Real
When this piece went up, the third fate was still just naming: Clotho spins, Lachesis measures, Atropos cuts, but Atropos herself wasn't running yet. Her workflow existed and reported green every night. It just wasn't doing anything.
The green was a lie. The Claude SDK call inside the workflow was failing immediately (is_error: true, an auth problem with the API key), but the action swallowed it and exited 0 anyway. Success status with zero work done, night after night. I parked it rather than debug in a hurry: fund the API key properly, or find another way in, later.
The fix ended up being simpler than funding credits: swap ANTHROPIC_API_KEY for a Claude Code OAuth token (claude setup-token, same mechanism the other fate agents already used) and pass it as claude_code_oauth_token in the workflow. No credits to buy. Atropos started actually running.
She's a different kind of agent from Clotho and Lachesis. They observe: file issues, score, never touch state. Atropos is the only fate with act trust: she closes what's gone stale.
- Stale issues: warns after
stale_issue_daysof inactivity, closes after a further grace period if nothing changes. One warning per window, never a repeat. - Stale PRs: warns, never closes. Humans close PRs.
- Stale branches: deletes branches Lachesis already flagged as cold, once the grace window clears. Never prunes on her own initiative.
- Inbox archival: opt-in, moves completed items to an archive section.
First real output landed 2026-07-08, on issue #153 ("Lachesis PR-diff mode (v0.3.0) — optional"): a warning comment after 30 days of inactivity, atropos:stale label attached.
👋 This issue has been inactive for 30 days. It will be closed in 14 days if there is no further activity.
To keep it open: remove the
atropos:stalelabel or addatropos:keep.Filed by atropos · 2026-07-08
Nobody touched it. 14 days later, exactly on schedule, she closed it.
Closing due to inactivity (44 days). If this is still relevant, reopen and add
atropos:keepto prevent future auto-closure.Filed by atropos · 2026-07-22
Small, boring, exactly the job. The atropos:keep exemption is real too, just not exercised on this one. On other issues it sits next to whatever the repo's other bots have already filed:

Is It For You?
Moirai is opinionated. It assumes:
- You're shipping LLM-driven automation across multiple repos.
- You'd rather decompose a workflow into thin specialised agents than build one monolithic "do-everything" agent.
- You're willing to put up with a small protocol spec in exchange for not maintaining five bespoke workflow files.
If you have one repo and one agent, Moirai is overhead. If you have five repos that all want the same scanner running, or one repo that wants Clotho and Lachesis and Atropos and a chained Conductor reading their outputs, the protocol is leverage.
The payoff compounds with both number of consuming repos and number of agents in the catalog. Add a new repo, install three agents in 30 seconds. Improve Lachesis's scoring rubric, every consuming repo benefits on the next pointer bump.
What's Next
Moirai is public from day one: github.com/derrybirkett/moirai, now well past its original v0.2.0 tag. The three registered agents are github.com/derrybirkett/clotho, github.com/derrybirkett/lachesis, and github.com/derrybirkett/atropos. Curator and Auditor stay in the registry marked retired: true, for provenance.
The immediate roadmap:
- Stories: the next layer up from the agent. Today each agent is one workflow file; a story would bundle several agents under one trigger with a shared budget and termination policy ("nightshift" as one file instead of three). Sketched in Moirai's own roadmap doc, not yet built.
- A second consumer: install Moirai in one of the standalone repos in my Delta Suite. The second consumer is where the real bugs surface.
- A meta-agent: read across
issuesandinboxsinks, propose cross-cutting action. The thin-agents-compound payoff, and the reason Stories has to come first: a meta-agent needs somewhere to be declared.
The framework reveals itself through the agents.