SapixDBSapixDB/Docs
Early Access
Phase 3 · Mutants

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.

Governed advisors, not autonomous actorsADR-004: Mutants produce reports and wait for approval. They have zero deploy authority. The human is always in the loop. On Zone 3 (Free) agents, proposals auto-approve after 4 hours — but that timer can always be interrupted by a reject.

How it works

The Mutant lifecycle has three phases:

1

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.

2

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.

3

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.

Zone 0 — ImmutableCore
Cannot be mutated under any circumstances. Used for genesis agents and compliance anchors.
Rejected
Zone 1 — Protected
Requires two separate administrators to approve before a proposal can be applied.
Dual-admin sign-off
Zone 2 — Governed
Requires one administrator to explicitly approve. Good default for production agents.
Single admin approval
Zone 3 — Free
Proposals auto-approve after a 4-hour window with no action. Used for dev/staging agents.
Auto-approve after 4 hours

Mutation kinds

typeFieldsEffect
SchemaVersionnew_version: u32Bump the agent's declared schema version. The cheapest mutation — signals downstream tooling to reindex or migrate.
ZoneChangenew_zone: 0–3Migrate the agent to a different trust zone. Cannot promote to ImmutableCore (Zone 0) via mutation.
BehaviorPatchdescription, patch_b64Propose a behavior change. Stored as an audit record in v1 — no execution effect. Used to document intent.
FieldAddfield_name, field_typePropose adding a field to the agent's payload schema. Audit-only in v1.
FieldRemovefield_namePropose removing a deprecated field. Audit-only in v1.

HTTP API

Propose a mutation

POST /v1/mutations
{
  "target_agent_id":   "orders-agent",
  "proposer_agent_id": "debt-mutant",
  "kind": {
    "type":        "SchemaVersion",
    "new_version": 3
  }
}
response
{
  "proposal_id":        "a1b2c3d4...",
  "status":             "pending",
  "policy":             "single_admin",
  "auto_apply_after_ms": null
}

List and inspect proposals

GET /v1/mutations
// Returns all proposals for this agent
{ "proposals": [ { "id_hex": "...", "status": "pending", ... }, ... ] }
GET /v1/mutations/:id
// 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

POST /v1/mutations/:id/approve
{ "approver_agent_id": "admin-alice" }
POST /v1/mutations/:id/reject
{ "rejector_agent_id": "admin-bob" }
POST /v1/mutations/:id/apply
// No body. Proposal must be in "approved" status.
// Writes a MUTATION-flagged audit record to the strand.
POST /v1/mutations/:id/reinit
// 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).

POST /v1/agent/cage
// No body. Marks the agent as caged.
DELETE /v1/agent/cage
// 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.

installation
pip install sapixdb-mutant
my_mutant.py
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.

installation
pip install sapixdb-mutant-debt
Python
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())
CLI
# 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

MethodDescription
run_cycle() → list[ProposeResponse]One observe → propose cycle. Safe to call from a scheduler.
run_loop(interval_secs=3600)Run cycles forever, sleeping between them.
observe_and_propose(obs) → list[dict]Abstract. Implement in subclass. Return mutation kind dicts.
list_proposals() → list[MutationProposal]Fetch all proposals from the target agent.
approve(proposal_id) → MutationProposalAdd this mutant's approval to a pending proposal.
reject(proposal_id) → MutationProposalReject a pending proposal.
apply(proposal_id) → ApplyResponseApply an approved proposal.
A2A tracesEvery successful proposal emits a 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.
Mutants never read strand dataThe 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.
Ready to add governed AI advisors to your agents?

Start with the Debt Mutant — one package, zero AI required, ready in 5 minutes.