| status | active | |
|---|---|---|
| owner | @nateshpp | |
| last_reviewed | 2026-07-08 | |
| canonical |
|
Modonome runs a small crew of agents under one principle applied at two governed checkpoints:
nothing advances without an independent check. A proposal reaches the backlog only past a check
of the proposal, and a diff reaches main only past an independent checker plus common CI. The
full flow is in adr/ADR-040-end-to-end-operating-model.md;
the structure decision is in adr/ADR-044-agent-org-structure.md.
This is the canonical reference: the roster, the config fields, how to route a role's spend, and
worked recipes. docs/agent-org.md and docs/ops/runner-model-config.md now redirect here. The
single source of record for the running values is always .modonome/config.yaml.
Every role is config-driven, not hardcoded: a runner, a model or a prioritized model fallback
chain, a cost class, optional skills/tools/schedule. A new crew role needs zero code changes
(scripts/agent/resolve-role.mjs falls back to a generic descriptor for any role name present in
config but absent from its built-in defaults). Combined with the provider registry's fail-closed
cost semantics (an unrecognized provider resolves to paid rather than silently escaping the
budget gate, scripts/agent/providers.mjs), this is a real per-role model-routing and
cost-control system, not a single hardcoded agent behind a marketing name.
| Agent | Kind | Purpose | Fires on | Recommended model (cost and fitment) |
|---|---|---|---|---|
maker |
producer | Implements one scoped work item behind a failing test | a work item in the backlog | claude-sonnet-4-6, with a local fallback. The price and quality balance for bounded diffs. |
checker |
integrity | Independent review of the diff plus the gates; must be a different model family from the maker | after the maker | claude-opus-4-8. Adversarial depth; the distinct-family rule is enforced (ADR-006). |
researcher |
producer | Grounded proposals from the repo and backlog (Checkpoint 1) | a schedule or a manual run | claude-sonnet-4-6, falling back to local-default. Grounded drafting does not need a frontier model. |
self-govern |
integrity | Modonome applying its own governance to itself | nightly schedule | claude-haiku-4-5-20251001. Cheap and frequent. |
envisioner |
producer | Scoped innovation proposals grounded in an owner-approved direction (DECISIONS.md, ROADMAP.md) |
weekly schedule (0 6 * * 1), independent-check gated like the researcher |
claude-opus-4-8, with a local fallback. Ideation benefits from a frontier model; the low weekly cadence bounds the cost. |
marketresearcher |
producer | Opt-in scan of the ecosystem and standards; sourced, paraphrased findings only. Named without a hyphen: scripts/agent/render-prompt.mjs requires a role name matching ^[a-z]+$ |
weekly schedule (0 7 * * 1), behind market_scan_enabled (default off) |
claude-haiku-4-5-20251001, with a local fallback. Cost-first, matching local_model_only_by_default. |
maker and checker are the first-class separation-of-duties pair and always run. The others are
crew roles that run on their own schedule or trigger and stay off the default role_sequence, so
a plain cycle still runs exactly the maker then the checker. researcher, envisioner, and
marketresearcher are configured today but not on the active sequence (WI-042, ADR-039/040):
they run only via an explicit --roles invocation or their own schedule, never as a side effect
of a default cycle.
Every agent is a key under roles in .modonome/config.yaml. The full field set is defined in
schemas/config.schema.json. No code change is needed to add or retune an agent.
runner:local(a self-hosted runner, for example a Mac running local models) orcontainer(anubuntu-latestrunner). Runner definitions live underrunners, mapping to labels ([self-hosted, mac-mini]forlocal) and the CLI binary to invoke.modelandmodels: the primary model, and an optional prioritized fallback list. A paid frontier choice can fall back to a free or local model when there is no budget or when the first choice is unreachable at runtime (isUnreachableErrorinrun-cycle.mjsretries only on network unreachability, never on a real answer, an auth failure, or a bad status). Model definitions and their providers live undermodelsandproviders; cost class and transport are resolved byscripts/agent/providers.mjs.skillsandtools: declarative capability tags read by the loop and prompts (ADR-039). They describe an agent; tool execution is gated separately.schedule:{ cron, timezone }, the declarative cadence for a scheduled crew role.trigger: a string shorthand (trigger: schedule) or an object{ type, after, cron }.typeis one ofschedule,manual,webhook,on-merge, orafter-role. Anafter-roletrigger withafter: [role, ...]chains this agent after those agents in one run; the order is a topological sort that keeps the maker then checker spine and fails closed on a cycle or a dangling role name before any model call.execution_target: the environment or runner that must run the agent, when its model endpoint is only reachable from one place (scripts/agent/route-action.mjs).
scripts/agent/triggers.mjs reads these fields, derives the execution order for after-role
chains (consumed by resolveRoleSequence in scripts/agent/run-cycle.mjs), and renders the
scheduled-crew workflow .github/workflows/modonome-schedule.yml, so a scheduled crew role never
needs a hand-edited workflow.
These are operator settings, not repository constants. The control panel's Agents tab
(apps/control-panel/) edits an agent's model chain, skills, and tools directly against
.modonome/config.yaml through the gated writer, so an agent's model is a control-panel choice
rather than a fixed one. See control-panel-modes.md for the panel's
permission model.
scripts/agent/providers.mjs maps a provider name to its transport, cost class, and auth
environment variable:
| Provider | Transport | Cost class | Auth env |
|---|---|---|---|
anthropic |
anthropic-cli |
paid | ANTHROPIC_API_KEY |
local |
openai-http |
local (no charge, no network dependency) | none |
github-models |
openai-http |
free (no charge, still remote) | GITHUB_TOKEN |
openai-compatible |
openai-http |
free | OPENAI_API_KEY |
An unrecognized provider name resolves to the paid/anthropic-cli default rather than throwing,
so an unknown provider never silently escapes the budget gate. A role's cost class is billable
only when it resolves to paid; remote_model_budget_usd_per_day: 0 plus
local_model_only_by_default: true means paid spend is structurally impossible until an owner
raises the budget.
Maker on a local model, checker on a frontier model, $0/day budget:
remote_model_budget_usd_per_day: 0
roles:
maker:
runner: local
model: local-maker
checker:
model: claude-opus-4-8 # stays paid-capable; budget gate blocks it until raised
models:
local-maker:
provider: local
base_url: http://localhost:11434The maker runs at zero cost; the checker is configured but cannot spend until an owner raises
remote_model_budget_usd_per_day above 0. require_distinct_maker_checker_model still enforces
that the two are never the same string, local or hosted alike.
A fully local, zero-cost maker/checker pair (useful for testing the loop against a local or free endpoint with no metered spend):
remote_model_budget_usd_per_day: 0
roles:
maker:
model: local-maker
checker:
model: local-checker
models:
local-maker:
provider: local
base_url: http://localhost:11434 # local server or free gateway
local-checker:
provider: local
base_url: http://localhost:11435 # a distinct endpoint strengthens model-family distinctnessFor providers other than Anthropic, run an Anthropic-compatible gateway (for example LiteLLM, or
an Ollama proxy) and point base_url at it. None of this changes the arming posture: the
autonomous loop stays off until an owner arms it (MODONOME_ARMED).
Add a custom crew role with zero code changes:
- Add a key under
rolesin.modonome/config.yamlwith amodelormodels, arunner, and ascheduleortrigger. A role absent from the built-in defaults inherits the generic fallback (container runner, the generic maker model) and resolves like any other role. - If the role runs as a session, add a prompt at
prompts/roles/<role>.txt. A role on the executed sequence without a prompt is rejected during planning, before any model call. - Dry-run it in isolation:
node scripts/agent/run-cycle.mjs --roles <role> --dry-run, to confirm the plan resolves the model chain and routes to a reachable target. - If the role is scheduled on a container runner, regenerate the workflow with
node scripts/agent/triggers.mjs --writeand commit the result. A crew role stays a dry-run by default and runs live only behind theMODONOME_SCHEDULE_EXECUTErepository variable.
- Both producer roles (
researcher,envisioner) feed Checkpoint 1 throughscripts/agent/review-proposals.mjs, so a proposal enters the backlog only on an explicit approval. - The maker and checker always use different model families (ADR-006), enforced in config and at
plan time. When
require_distinct_maker_checker_modelistrue(the default),validate-configfails ifroles.maker.modelandroles.checker.modelare the same string, hosted or local alike:node scripts/validate-config.mjs .modonome/config.yaml. - The whole loop stays off until an owner arms it (
MODONOME_ARMED), and paid model spend stays at zero until an owner raisesremote_model_budget_usd_per_day.
scripts/agent/resolve-role.mjs exports resolveRole(cfg, role), which reads the config maps and
returns { runner, runnerLabels, cliPath, model, modelProvider, modelBaseUrl }. CLI flags override
the returned values in later pipeline stages.
The committed models here are recommendations for cost and fitment. The running values are
whatever .modonome/config.yaml holds, and the roadmap for the planned roles is Milestone 5 in
../ROADMAP.md.