Mutants
Mutants are governed AI advisors that observe agent metadata and propose structural improvements. They never read strand data. They never deploy anything on their own. Every proposal waits for human approval — the right zone policy gates how much approval is required.
How it works
The Mutant lifecycle has three phases:
Observe. The Mutant calls /v1/status on the target agent. This is the only data it reads — metadata like record count, schema version, active profile, and cage status. No nucleotide data, ever.
Propose. Based on what it observes, the Mutant posts a mutation to POST /v1/mutations. The engine stores the proposal and returns a proposal_id and the governing policy.
Apply. Once enough humans approve (zone policy determines how many), the proposal is applied with POST /v1/mutations/:id/apply. An immutable audit record is written to the strand with a MUTATION flag.
Zone approval policies
Every agent is assigned a Zone. The Zone determines how many approvals a mutation proposal needs before it can be applied.
Mutation kinds
HTTP API
Propose a mutation
{
"target_agent_id": "orders-agent",
"proposer_agent_id": "debt-mutant",
"kind": {
"type": "SchemaVersion",
"new_version": 3
}
}{
"proposal_id": "a1b2c3d4...",
"status": "pending",
"policy": "single_admin",
"auto_apply_after_ms": null
}List and inspect proposals
// Returns all proposals for this agent
{ "proposals": [ { "id_hex": "...", "status": "pending", ... }, ... ] }// Returns one proposal by ID
{
"id_hex": "a1b2c3d4...",
"target_agent_id": "orders-agent",
"proposer_agent_id": "debt-mutant",
"kind": { "type": "SchemaVersion", "new_version": 3 },
"status": "pending",
"proposed_at_ms": 1716400000000,
"approvals": [],
"rejector": null
}Approve / reject / apply
{ "approver_agent_id": "admin-alice" }{ "rejector_agent_id": "admin-bob" }// No body. Proposal must be in "approved" status. // Writes a MUTATION-flagged audit record to the strand.
// Re-create a Rejected proposal with the same target + kind. // Useful after a proposal is rejected but the underlying issue persists.
Caging an agent
Caging marks an agent as restricted — a Control Plane signal visible to operators and respected by the Mutant base class (caged agents are skipped during observation cycles).
// No body. Marks the agent as caged.
// Removes the cage flag.
Python — building a Mutant
Install the base package and subclass SapixMutant. Implement observe_and_propose to return a list of mutation kind dicts based on the observation.
pip install sapixdb-mutant
from sapix_mutant import SapixMutant, AgentObservation
class HighWaterMutant(SapixMutant):
"""Propose a schema bump when records exceed 50,000."""
async def observe_and_propose(self, obs: AgentObservation):
if obs.record_count >= 50_000:
return [{"type": "SchemaVersion", "new_version": obs.schema_version + 1}]
return []
# One-shot cycle
import asyncio
mutant = HighWaterMutant(
target_agent_id="orders-agent",
proposer_id="high-water-mutant",
)
asyncio.run(mutant.run_cycle())
# Or run continuously every hour
asyncio.run(mutant.run_loop(interval_secs=3600))Debt Mutant — built-in, ready to use
The Debt Mutant ships with SapixDB. It watches record count and schema version lag and proposes SchemaVersion bumps when either threshold is exceeded.
pip install sapixdb-mutant-debt
from sapix_mutant_debt import DebtMutant
import asyncio
mutant = DebtMutant(
target_agent_id="orders-agent",
proposer_id="debt-watcher",
record_threshold=10_000, # propose after 10k records
schema_lag_threshold=2, # propose if 2+ versions behind
target_schema_version=5, # we want schema v5
)
asyncio.run(mutant.run_cycle())# Run once sapix-mutant-debt --target orders-agent --once # Run on a 1-hour loop sapix-mutant-debt --target orders-agent --interval 3600 --record-threshold 10000
SapixMutant API reference
sapix/mutant/proposal event to the A2A observability service (if A2A_URL is configured). This gives operators a full audit trail of what every Mutant proposed, when, and against which agent.SapixMutant base class only calls /v1/status — the metadata endpoint. There is no method on the base class that reads nucleotides, payloads, or any business data. If you need to read data in a subclass, you must explicitly create a separate SapixClient and take responsibility for that access in your Codios policy.Start with the Debt Mutant — one package, zero AI required, ready in 5 minutes.