Skip to content

Snodo

Snodo is an AI-SDLC protocol engine. It runs an MCP loop around the coding agent you choose: validate a task, dispatch it, and inspect its asynchronous job; author plans and queues only when the work needs that structure. Protocol policy defines the validators and capabilities, while the engine enforces the task loop and records decisions.

For live job watching, open the browser link returned by MCP when an HTTP or managed tunnel URL is configured, or run snodo logs <job_id> --watch. The watch_job MCP Apps panel is an optional host feature; Claude Desktop and relayed sessions may return only its plain-text snapshot.

Optional job notifications support ntfy, generic webhooks, Slack, Discord, and Teams. Configure them in ~/.snodo/config.yml (or $SNODO_HOME/config.yml) and test targets with snodo config --notify-test. See the user configuration reference. For a short introduction, see the repository README.

Core idea: policy vs mechanism

Declare what a valid development process looks like (protocol.yml — modes, validators, constraints, disagreement policies). The engine enforces it structurally: tokens issue only when validators agree, the loop executes a task only on a valid token, and a single blocker halts execution unconditionally regardless of policy. Tool access at the MCP surface is the protocol's and the mode's to decide — not a token the caller must hold. You write the policy; the engine provides the mechanism.

2+N model

Two human-in-control roles — producer (code generation) and reviewer (integration) — with structurally separated approval authority. Plus N specialised AI agents that operate within those roles. The model is validated at load time: an approval-conferring tool (approve, merge) held by two modes causes a WF1 violation and the protocol won't load.

Architecture →

Get started

Use the canonical first-run quickstart in the runbook. It creates and commits a starter file before initializing Snodo, then runs a deterministic hello-world task through the governed task loop.

Runbook → — install, configure, CLI usage, MCP serving, and troubleshooting. Command reference → lists the current commands and options.

User configuration reference → — ~/.snodo/config.yml, provider credentials, model tuning, cloud and notification settings.

Running Snodo in CI → — validate pull-request and merge-request changes in GitHub Actions or GitLab CI.

Coder backends

The coder writes; snodo governs, gates and records. litellm (default), opencode-cli, codex-cli, agy and mock are supported; the container opencode path is experimental. Which coder you pick does not change what is enforced.

Coder backends →

Plans

Plans are multi-wave work graphs you author, not generate. Dependencies run between waves, not tasks, and a plan is re-verified on every load.

Authoring a plan → · Hand-authored plan runbook →

Machine interface

--json on status, mode show, session show, task show, and worktree list, plus snodo validate for running a phase's validators without a coder. Versioned with a schema field and validation-outcome exit codes (ADR 022).

Machine interface →

Follow work

MCP work starters return job IDs immediately. Watch the job in a browser using the returned link (typically through the configured tunnel), or use snodo logs <job_id> --watch; query status and logs when you need specific details. Notifications are detached, opt-in, and best-effort.

The language

Every enforcement rule is declared in protocol.yml — modes, validators, severity caps, constraints, and disagreement policies. The full DSL reference is one document.

Protocol reference →

Design rationale

Why PyJWT over custom HMAC? Why does warn withhold approval? Why ESCALATE → resolve → resume rather than auto-retry? Every design decision is documented as an ADR, extracted from the development audit log.

Decisions →

Project status

Actively-developed research implementation (beta). This table is the measured state of the code, not a promise.

Code ~32,700 lines of code across the uv workspace packages plus the root CLI/TUI
Complexity average cyclomatic complexity A (4.96) over 1,728 blocks (radon)
Lint / architecture ruff clean; package layering enforced in CI by import-linter
Tests ~3,900 collected; property-based tests over randomised inputs for the enforcement invariants
Python 3.12 and 3.13 (CI matrix)

The enforcement invariants — token integrity, capability boundaries, non-overridable blockers, audit completeness — are verified by property-based tests over randomized inputs.

Research

The paper is under review; its architectural claims are mapped to code and tests in Research. Empirical studies live in studies/.