Skip to content

Repository files navigation

MADE by Underpass

Part of Underpass AI — memory, coordination, and execution infrastructure for reliable AI agents.

MADE is a ceremony-driven multi-agent deliberation engine. The name is the description: Multi-Agent Deliberation Engine.

Event-driven coordination plane for councils of specialist agents. It runs structured deliberations (propose → peer-critique → revise → validate → score → winner) and longer declarative YAML ceremonies as explicit state machines, enforces output contracts on what agents produce, can score with an LLM judge, and ships observability that shows why an answer won. Domain- and provider-agnostic (vLLM, Anthropic, OpenAI, local, rule-based, human-in-the-loop). Kubernetes-first.

Lineage: started as a domain-agnostic Rust port of the swe-ai-fleet orchestrator service, and has since grown well beyond that port. It shipped as Underpass Choreographer until the rename to MADE; that was a naming change only — see CHANGELOG.md for every moved surface (crates, proto package, MCP tools, environment variables, chart, image).

Start here — pick an edition

MADE ships as two editions. They share made-core, made-app, the domain invariants and the workspace release version; the embedded facade calls the same application use cases the deployable binary calls. Full comparison: docs/editions.md.

Embedded edition — inside your coding agent

The ceremony engine runs in-process over MCP stdio. No service, no gRPC, no NATS, no database, no provider credentials.

cargo install made-mcp

The embedded backend requires a state file: where ceremonies survive a restart is an operator decision, never a default this crate invents.

mkdir -p "${XDG_STATE_HOME:-$HOME/.local/state}/underpass-made"
MADE_MCP_BACKEND=embedded \
MADE_MCP_REDB_PATH="${XDG_STATE_HOME:-$HOME/.local/state}/underpass-made/ceremonies.redb" \
  made-mcp

Claude Code:

claude mcp add made --scope user \
  --env MADE_MCP_BACKEND=embedded \
  --env MADE_MCP_REDB_PATH="$HOME/.local/state/underpass-made/ceremonies.redb" \
  -- ~/.cargo/bin/made-mcp

Codex CLI (~/.codex/config.toml):

[mcp_servers.made]
command = "/home/YOU/.cargo/bin/made-mcp"
env = { MADE_MCP_BACKEND = "embedded", MADE_MCP_REDB_PATH = "/home/YOU/.local/state/underpass-made/ceremonies.redb" }

Note that both registrations above name the same store. The default engine takes one process at a time, so running both hosts at once means the second one gets no ceremony tools at all. To share one store between them, build with --features sqlite and either start there or convert what you already have — sharing one ceremony store between two agent hosts has the recipe and what it costs.

Want the design-ceremony and run-ceremony skills too — install the MADE plugin. In Claude Code that is one line:

/plugin marketplace add underpass-ai/plugins
/plugin install made@underpass

Its launcher picks the state path for you and imports a pre-rename Choreographer store on first start, so none of the environment above is needed. It runs bin/made-mcp from a release package when there is one, and otherwise falls back to the made-mcp you installed on PATH. The same marketplace also carries the sibling kmp@underpass.

Read capability truth before you build on what the embedded surface reports.

Cluster edition — a deployed service

The full underpass.made.v1 gRPC contract, optional NATS messaging and Postgres persistence, provider-backed agents, the LLM judge, Prometheus metrics and OTLP traces. Helm chart with pinned images and hardened manifests.

MADE_NATS_ENABLED=false MADE_SEED_SPECIALTIES=triage just run
# gRPC on localhost:50055, in-memory persistence, one exercisable council

Deploy guide: docs/operations/deploy-kubernetes.md.

What it does

  • Structured deliberation — a strict one-way FSM per council run (Proposing → Revising → Validating → Scoring → Completed) with deterministic peer critique between agents and a bounded, total-order score for ranking.
  • Declarative ceremonies — longer multi-step meetings defined in YAML as explicit state machines (states, transitions, guards, retries, leases). The winning contribution of every step is an API artifact — a meeting record with a Mermaid diagram of the conversation — not a log line.
  • Output contracts — JSON Schema plus field rules enforced by shipped validators, with deterministic rejection (NoValidProposal) when no proposal satisfies the contract: unsupported output does not become a decision.
  • Optional LLM-as-judge — judge-aware scoring with fail-fast configuration, plus a judge discrimination metric that tells you whether the judge actually re-ranks proposals or just burns tokens.
  • Deliberation-native observability — every deliberation is a replayable OpenTelemetry trace (the debate itself, span by span, exported over mTLS) and Prometheus metrics designed for this domain: winner-score distribution, NoValidProposal rate, per-step ceremony outcomes. See docs/made-observability-design.md.
  • Two surfaces — a contract-first gRPC API, and a stdio MCP server exposing the same RPCs 1:1 to coding agents (Codex CLI, Claude Desktop), with embedded-only ceremony controls and read-only Markdown reports where no remote RPC exists.

Verify capabilities before making claims

“MADE supports X” is incomplete unless the statement identifies the running distribution and backend, the tools exposed by that executable, who performs external work, and which state survives a restart.

For MCP sessions, inspect the active tools/list result and start with made_discover_capabilities when it is available. Its backend-filtered catalog is authoritative for the installed executable surface. Discovery does not prove that a real step handler, durable store, credentials, or external authority have been configured.

See the capability-verification runbook before documenting or automating an integration.

The Underpass platform

Three planes, three repos:

Plane Repo Brand name Role
Memory + context kmp Underpass KMP (Kernel Memory Plane / Kernel Memory Protocol) One possible producer of LLM-ready context bundles from a typed knowledge graph.
Coordination this repo MADE by Underpass (Multi-Agent Deliberation Engine) Composes councils, runs deliberations, validates outputs, hands winners to an executor.
Execution + governed tools underpass-runtime Underpass Runtime Sessions, governed tool invocations, artifacts, policy decisions.

MADE is agnostic and independently usable. It does not depend on KMP, PIR, or any downstream product. It accepts caller-supplied ExternalContextBundles from any context source; KMP is one studied producer, not a required dependency. Runtime execution is optional via the RuntimeExecutor adapter. MADE does not embed any product vocabulary (no stories, plans, incidents, claims hardcoded) — all that is injected via configuration and proto messages.

Where the documentation lives

Run Locally Without External Services

Installation and host wiring are in Start here; this section is the developer loop for running the pieces from a checkout.

The service, with nothing behind it. In-memory persistence, noop messaging, the default noop executor. No NATS, Postgres, Runtime, KMP, PIR, or provider credentials. MADE_SEED_SPECIALTIES=triage makes it exercisable end to end immediately.

MADE_NATS_ENABLED=false MADE_SEED_SPECIALTIES=triage just run
# without just:
MADE_NATS_ENABLED=false MADE_SEED_SPECIALTIES=triage cargo run --locked -p made

The real ceremony engine over MCP, no service. The embedded backend fail-fasts without MADE_MCP_REDB_PATH: where ceremony state survives a restart is an operator decision, never a default this crate invents.

MADE_MCP_BACKEND=embedded \
MADE_MCP_REDB_PATH="$PWD/target/ceremonies.redb" \
  cargo run --locked -p made-mcp

MCP client wiring with no engine at all. Fixture mode returns deterministic canned responses; it must be selected explicitly and is not a live integration test.

MADE_MCP_BACKEND=fixture made-mcp

MCP against the local service from a second terminal:

MADE_MCP_GRPC_ENDPOINT=http://127.0.0.1:50055 made-mcp

Backend matrix and TLS configuration: docs/operations/mcp-stdio.md.

Workspace

Crate Purpose
made-core Domain types, ports, events. No IO.
made-app Use cases / application services.
made-adapters NATS, gRPC clients, config, external integrations.
made-embedded In-process ceremony facade with local defaults and injectable port adapters.
made-proto Tonic-generated gRPC code (underpass.made.v1).
made-mcp-proto Vendored underpass.made.v1 proto crate used to publish made-mcp independently.
made Binary: wires adapters, runs gRPC + NATS.
made-mcp Stdio MCP adapter with live-gRPC, embedded ceremony, and deterministic fixture backends.
made-e2e-runner Operator + E2E driver binaries (incl. made-run-ceremony) that exercise the service over its public gRPC surface.
made-tests-integration Integration tests backed by testcontainers-managed services. Not shipped.
made-consumer-smoke Standalone NATS consumer smoke check. Not shipped.

Principles

This project follows the same discipline as its siblings underpass-runtime and kmp:

  • Honest documentation. No marketing claims in code, docs, or commit messages. If a capability is not implemented and exercised, it is not described as if it were. "Planned", "in progress", and "prototype" are said out loud.
  • Everything is demonstrable and measurable. Any claim about behaviour, performance, or quality must be backed by a reproducible test, benchmark, or experiment that lives in this repository and runs in CI. No hand-wave numbers. No unsubstantiated quality claims.
  • Scientific method for iteration. Changes that alter behaviour follow: (1) hypothesis, (2) experiment design, (3) measurement, (4) result, (5) conclusion — recorded under docs/experiments/. We keep null results too.
  • Use-case agnostic. No vocabulary of any particular domain (software engineering, clinical, supply chain, …) leaks into MADE.
  • Provider-agnostic. No LLM vendor (vLLM, Anthropic, OpenAI, local, rule-based, human-in-the-loop) is privileged over any other.
  • API-first. The gRPC (crates/made-proto/proto/…) and AsyncAPI (specs/asyncapi/…) specifications are the source of truth. Generated code follows; breaking changes must be detected by the contract gate before any Rust code compiles.
  • Distribution via containers and Helm. Images are built under Dockerfile (podman and docker supported); deployment is via the Helm chart under charts/made/.
    • Pinned images only (a latest tag is refused unless development.allowMutableImageTags is set)
    • Non-root pod + container security contexts (runAsNonRoot, readOnlyRootFilesystem, ALL capabilities dropped, seccompProfile: RuntimeDefault)
    • automountServiceAccountToken: false (the binary does not call the Kubernetes API)
    • emptyDir on /tmp so any library tempfile write survives the read-only root filesystem
    • networkPolicy.enabled opt-in restricts inbound to the pod's declared ports and outbound to DNS, NATS, Postgres, and OTLP (plus any extra rules operators add)
    • MADE_POSTGRES_URL sourceable via valueFrom.secretKeyRef so the DSN never lands in values files
    • Optional PodDisruptionBudget gated on pdb.enabled
    • Chart-render CI (scripts/ci/helm-lint.sh) exercises every hardening feature and refuses a manifest that drops one.

Quality gates

  • Unit coverage: minimum 80 % of lines, target band 80–90 %, enforced by scripts/ci/rust-coverage.sh.
  • Integration tests: testcontainers-backed, real services per run (no mocks at the integration boundary).
  • End-to-end tests: a runner container drives scenarios either via docker compose or as a Kubernetes Job against a kind cluster with the Helm chart installed (contract-true path). Both paths are manual only, launched from the repository with make e2e-compose or make e2e-kubernetes — the per-PR gates (clippy, test, contract, integration-nats, integration-postgres, container-image, helm-chart) already cover the compile-and-unit surface; E2E is reserved for pre- release validation.

Status

What runs today (enforced by CI, every claim is backed by a test or gate in this repository):

  • made binary starts, reads config from MADE_* env vars, and serves the full underpass.made.v1 gRPC contract.
  • Implemented RPCs: every RPC in the underpass.made.v1 contract is backed by a use case — Deliberate, StreamDeliberation, GetDeliberationResult, Orchestrate, CreateCouncil, ListCouncils, DeleteCouncil, RegisterAgent, UnregisterAgent, ProcessTriggerEvent, RunCouncilDecision, RegisterContract, ListContracts, DeleteContract, RunCeremony, GetStatus, and GetMetrics. No RPC returns UNIMPLEMENTED. Caveats: (a) provider-backed RegisterAgent kinds require the matching Cargo feature and boot-time credentials; noop is always available. (b) StreamDeliberation emits phase transitions + a final DeliberationResult frame, not per-proposal/critique/revision events.
  • Optional NATS messaging: when MADE_NATS_ENABLED=true, the service publishes all 5 outbound events (made.task.*, made.deliberation.completed, made.phase.changed) and consumes inbound TriggerEvents from made.trigger.>. Otherwise a no-op messaging adapter is wired.
  • Optional seeding: MADE_SEED_SPECIALTIES=triage,reviewer registers one NoopAgent and one single-agent council per specialty so a fresh deployment is immediately exercisable end-to-end.
  • Ceremony orchestration: RunCeremony executes a YAML-defined ceremony as a finite-state machine — states, steps with pluggable handlers, guarded transitions, and roles. A step can drive a full council deliberation; prior turns thread into later steps' briefs; the response carries a Mermaid sequence diagram of the conversation. Catalog ceremonies (daily standup, technical debate, sprint planning, speaker + Q&A) run end-to-end in CI.
  • Scoring: the winner of a deliberation is chosen by a pluggable ScoringPort. The default ranks by validator pass-fraction; an optional LLM-as-judge (MADE_JUDGE_ENABLED, with MADE_JUDGE_THRESHOLD) instead rates each proposal's intrinsic quality and makes that the score. Disabled by default, and fail-fast: enabling it without a vLLM endpoint/model refuses to start rather than silently degrading.

Persistence:

  • When MADE_POSTGRES_URL is set, deliberations, councils, the agent registry, and operational statistics persist to Postgres; otherwise the in-memory defaults are wired. Persistence choice is binary: every backing is either Postgres or in-memory together, so no replica reads from a split source of truth. Migrations apply on startup — a fresh cluster is immediately exercisable. Schema lives under crates/made-adapters/migrations/postgres/.
  • Agents persist as descriptors (id, specialty, kind, attributes); live AgentPort handles are rehydrated through the wired AgentFactoryPort on resolve, so no pickled provider state crosses the database boundary.
  • Statistics counters use an INSERT ... ON CONFLICT DO UPDATE ... x = x + 1 protocol so concurrent replicas accumulate into the same row without a read-modify-write race — verified by a 50- concurrent-record integration test.

Agent factory (provider-backed materialization):

  • The binary wires DispatchingAgentFactory, which materializes kind == "noop" unconditionally plus any provider whose Cargo feature is compiled in AND whose credentials are present at boot:
    • agent-anthropic + MADE_ANTHROPIC_API_KEYkind=anthropic
    • agent-openai + MADE_OPENAI_API_KEYkind=openai
    • agent-vllm + MADE_VLLM_MODEL + MADE_VLLM_ENDPOINT (+ optional MADE_VLLM_BEARER_TOKEN) → kind=vllm Per-descriptor overrides via provider.model, provider.endpoint, provider.max_tokens attributes on the registered descriptor. Startup log emits agent_kinds= listing every kind the binary will accept on RegisterAgent.

Caveats and observability:

  • Prometheus metrics: the binary serves an operational metric surface at GET /metrics (HTTP port 8080) through a domain MetricsRecorderPort and a PrometheusMetricsRecorder adapter (explicit registry, no global recorder), exposed alongside the original Statistics-backed counters. The families cover deliberation quality (duration, winner-score distribution, terminal outcome), the LLM judge (latency, score, errors by kind, discrimination — does the judge re-rank or just burn tokens? —, tokens, scoring mode), the proposing providers (request latency, errors, in-flight gauge for vLLM serial saturation, tokens), the ceremony engine (outcomes, durations, per-step status, blocked transitions), NATS publish, and the Postgres pool. Recording is a synchronous, infallible side-channel that can never block or fail a deliberation. See docs/made-observability-design.md for the catalogue, alerts, and dashboard design. (Deferred so far: gRPC front-door RED — already covered by the request traces — and per-query Postgres latency.)
  • StreamDeliberation streams phase transitions only; per-proposal, per-critique, and per-revision streaming arrives in a later slice.
  • Distributed tracing: the core use cases, gRPC handlers, NATS inbound subscriber, and AutoDispatchService emit #[tracing:: instrument] spans with domain fields (task_id, specialty, event_id, agent_id, kind). A regression test pins the deliberate span name and fields.
  • W3C Trace Context propagation across NATS: every outbound event carries a traceparent header stamped by the publisher (TraceContext::generate() when no upstream context is present). The inbound subscriber extracts trace_id and span_id from the header and surfaces them as fields on the nats.trigger.inbound span. Integration-tested against a real NATS container.
  • W3C Trace Context propagation across gRPC (opt-in via the otel Cargo feature): every RPC handler calls link_span_to_metadata, which reads traceparent from request metadata and sets it as the OTel parent context of the current tracing span. Integration-tested via a tracing-opentelemetry bridge.
  • OTLP exporter (opt-in via the otel feature + runtime MADE_OTLP_ENDPOINT): when both are present the binary installs a batching OTLP/gRPC exporter and layers the tracing-opentelemetry bridge into the subscriber, so every instrumented span ships to the configured collector with real OTel trace/span IDs. Feature off → the binary has zero OTel dependency surface. Endpoint unset → the exporter is not wired (no silent background connections).

See docs/experiments/ for anything beyond these bullet points.

Legal

Copyright © 2026 Tirso García Ibáñez.

This repository is part of the Underpass AI project. Licensed under the Apache License, Version 2.0, unless stated otherwise.

Redistributions and derivative works must preserve applicable copyright, license, and NOTICE information.

Original author: Tirso García Ibáñez · LinkedIn · Underpass AI

About

MADE by Underpass — ceremony-driven multi-agent deliberation engine. Declarative YAML ceremonies, structured deliberation with peer critique, output contracts, LLM-as-judge scoring and auditable decision traces (OpenTelemetry + Prometheus). Use-case agnostic, provider-agnostic, API-first. Rust.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages