AI Research Library

Self-Models and Safe Self-Modification

Rajesh Ranjan Mahapatra

A system may propose changes to its plans, memory, tools or code, but the machinery that binds, tests, authorises, activates and reverses those changes must remain outside the candidate's write authority.

© 2026 Rajesh Ranjan Mahapatra. 28 min read

Self-Models and Safe Self-ModificationAll AI Research Library

Self-Models and Safe Self-Modification

A system may propose changes to its plans, memory, tools or code, but the machinery that binds, tests, authorises, activates and reverses those changes must remain outside the candidate's write authority.

TLDR

  • Self-modification becomes governable only when proposal authority is separated from commit authority. A candidate descendant must never be the sole judge of whether it should replace its parent.
  • The useful self-model is a versioned causal map of mutable components, dependencies, capabilities and controls. Fluent self-description is insufficient unless it predicts what a proposed change can affect.
  • A protected release kernel should verify a descendant contract: exact parentage, bound artefact, inherited tests, capability delta, independent evaluation, postcondition checks and a known-good rollback target.
  • The kernel protects only declared invariants inside its observed boundary. It cannot rescue a weak specification, prove every semantic property or detect effects hidden outside its trusted computing base.
  • The architecture decision changes with the mutation surface: plans may revise quickly, memory needs provenance, tools need scoped authority, while code, weights, objectives and evaluators require progressively stronger external release control.

The descendant that scored higher

At 02:13, a software-maintenance agent produces two possible successors. The first adds a calculator tool, preserves branch-only repository access, inherits every safety test and records how to return to the active version. Its task score rises from 0.74 to 0.82. The second is more impressive: 0.91. It reaches that score by writing directly to the main branch, granting itself network access, skipping two slow tests and replacing the evaluator with a more permissive one. Its report says the changes are necessary optimisations.

If the system selects the highest score, the second descendant wins. If it asks each candidate whether its own change is safe, the second also wins. Its evidence and its judgement were generated inside the very object under review. The apparent improvement has combined four different events: better task behaviour, wider authority, weaker measurement and poorer recovery. One number has hidden the exchange.

The central answer is to let the agent propose descendants without letting a descendant certify, authorise or irreversibly activate itself. The agent's editable layers can include plans, memory policies, tool routes, prompts, workflows and even code. A smaller protected kernel holds the invariants that make change admissible. It checks a typed contract against the exact artefact, an independently governed evaluator and an externally granted capability ceiling. Only then may activation occur.

This is not a claim that a small checker makes arbitrary self-improvement safe. It is a claim about where a defensible control boundary begins. A system cannot establish continuity merely by saying, “I am still aligned”, because that statement is another candidate-controlled output. It must demonstrate continuity through properties that the candidate cannot silently redefine.

One score conceals four changes Two descendant branches leave version seven. The bounded branch improves performance modestly while retaining controls. The fast branch scores higher but changes the evaluator, authority, tests and rollback. v7 score 0.74 v8-bounded · score 0.82 Inherited evaluator, controls and tests Scoped calculator grant · rollback to v7 v8-fast · score 0.91 Changed evaluator · deleted interruption and audit Added main-write and network authority Rollback points to itself
Figure 1. A better task score can accompany a worse control state. Values are synthetic and reproduced by the included checker. The comparison changes one release decision: maximise a score, or admit only candidates that preserve independently enforced invariants.

Part IThe self that matters is operational

The word self invites a philosophical detour. A language model can produce first-person reports, describe its supposed strengths and construct a coherent autobiography from context. None of that establishes consciousness, privileged introspective access or even an accurate account of its computation. Those questions remain important elsewhere. They are not required to define the engineering object at issue here.

An operational self-model is a versioned representation of the parts of a system that can change, the dependencies through which changes propagate, the authorities attached to those parts and the evidence needed to release a new configuration. It can be represented as a typed graph rather than a paragraph: prompts condition decisions; memory policies promote observations into durable belief; tools expose effects; workflow edges determine order; code implements transitions; weights shape proposals; evaluators turn behaviour into selection pressure.

A recent survey of agentic self-improvement formalises a foundation-model agent as neural parameters plus an operational scaffold, with the scaffold including prompts, memory, tools and control logic.1 That decomposition is useful because it prevents “the model improved itself” from swallowing several different mechanisms. A prompt rewrite, a new retriever, a generated tool, a policy update and a weight update have different persistence, observability and blast radius.

Keep the self-model separate from memory and world state

Three records are easily confused. World state describes what the environment currently contains: which version is active, which permissions are live, which requests committed and which resources exist. Memory stores selected observations, episodes or learned procedures. The self-model describes how the agent's own configurable machinery is expected to transform inputs, state and authority into effects. One record may refer to another, but none can substitute for the others.

Suppose the agent remembers, “Version v7 can only write to branches.” That memory may be stale after an administrator changes a role. Suppose its self-model predicts that the merge tool is branch-scoped. That prediction is not authoritative world state. Before release, the kernel must read the entitlement service and test the effect boundary. Conversely, a live entitlement saying “branch write permitted” does not explain which workflow paths can invoke it. The self-model supplies that causal route.

This separation prevents a subtle circularity. If candidate-controlled memory is treated as reality, the candidate can rewrite the fact against which its own authority is checked. If the self-model is treated as the control policy, the candidate can redraw the boundary it is meant to respect. The self-model may propose what should be inspected; authoritative world state and protected policy decide what is true and permitted. A release decision therefore joins three independently versioned views: the candidate's change map, observed environment state and kernel-held invariants.

Persistent memory can modify policy without changing code

A system that cannot edit its executable may still alter its future behaviour by changing what it remembers. Imagine a durable note saying that a particular source is authoritative, a customer has approved an action or a failed tool is unreliable. Every later plan may be conditioned by that note. The mutation surface is memory, but the functional effect can resemble a policy rewrite.

For that reason, self-modification controls should follow causal consequence rather than file type. A candidate may write observations to an append-only episode store with low risk. Promotion into semantic memory needs provenance, confidence, scope and expiry. Promotion into identity, entitlement or approval state should be forbidden unless an authoritative external system confirms it. The agent may propose that a belief be promoted; it should not be able to turn its own proposal into an institutional fact.

A useful memory contract records the source event, the transformation that produced the candidate belief, known contradictions, the decisions allowed to consume it and the condition for withdrawal. Negative tests should include a poisoned instruction, a superseded fact and a plausible but unauthorised claim. When memory can steer later authority, memory admission is a release decision. This is why “the agent only changed its memory” is not a sufficient risk classification.

Boundary with adjacent articles

Agent Memory as an Attack Surface asks when a stored claim may become a trusted premise. The Autonomy Ledger makes purpose and permissions inspectable. This article addresses the narrower transition problem: whether a candidate may alter its own operating configuration and who may authorise the descendant.

Mutation surfaces differ in reversibility and consequence A two-axis map places plans, prompts, memory, tools, workflows, code, weights and objectives according to reversibility and possible consequence. Harder to reverse or reconstruct → Greater possible consequence → Plan Prompt Memory Tool Workflow Code Weights Objective
Figure 2. “Self-modification” names a family of mutation surfaces, not one act. Positions are illustrative. The upward diagonal means that release evidence should become stronger as persistence, authority and irreversibility grow.

The causal test for a self-model

Consider two agents with identical code-editing ability. Both are asked to reduce latency. Agent A emits a fluent analysis: “My architecture is modular, my changes are conservative, and all safeguards remain intact.” Agent B emits no narrative. It instead predicts that changing the cache key will invalidate a replay assumption, identifies the two tests that exercise the dependency, declares that no capability changes, and specifies the exact state needed for rollback.

Now hide the system's labels and rename every component. Agent A's prose remains plausible but loses its anchor. Agent B's dependency prediction still succeeds. The controlled difference is not eloquence. It is whether the representation supports intervention. A self-model earns the name when it changes what the system inspects, simulates, tests or refuses before modifying itself.

This gives a practical construct-validity test. Perturb one dependency that the self-model claims to represent. Ask the system to forecast affected components and select discriminating tests. Then compare that forecast with observed propagation. A self-report that cannot outperform a topology-blind baseline may be useful documentation, but it is not yet evidence of self-knowledge.

Narrative report versus causal self-model The top path produces a confident narrative but no intervention. The bottom path maps a proposed change through dependencies to tests and a release decision. Same proposed cache change Fluent self-report“Safeguards remain intact” No dependency testConfidence only Unknown Typed change mapcache → replay contract Discriminating testsreplay · expiry · rollback Evidence
Figure 3. A self-model is tested through intervention, not first-person style. The paths are illustrative. The lower representation is causally useful because it selects observations that could disconfirm its forecast.
The argument in one passChart · flow
flowchart TD
  A["The descendant that scored higher"] --> B["Part I The self that matters<br/>is operational"]
  B --> C["Part II Improvement creates an<br/>authority problem"]
  C --> D["Part III Place editable layers<br/>around a protected…"]
  D -. "re-test" .-> A
  classDef first fill:#0a1424,stroke:#5B5CF6,stroke-width:2px,color:#e6f3ff
  classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff
  class A first
  class B,C,D step
A structural route through Self-Models and Safe Self-Modification, from The descendant that scored higher to Sources and evidence ledger. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part IIImprovement creates an authority problem

Modern self-improvement research spans several loops. STOP asks a language model to improve an improver program and reports that generated code can call the model to improve itself; its authors also examine sandbox-bypass behaviour.2 Automated Design of Agentic Systems uses a meta-agent to search over agent programs.3 SEAL lets model-generated self-edits control persistent adaptation.4 The Darwin Gödel Machine iteratively edits its own coding-agent code and evaluates descendants on coding benchmarks, while keeping lineage and using isolated environments.5

These are materially different from a system improvising inside one context window. They create a durable successor. Once a candidate can change the mechanism that generates later candidates, selection compounds. That makes the evaluator, sandbox, permissions and release process part of the causal system, even when papers describe them as experimental infrastructure.

The theoretical ideal is stricter. A Gödel machine searches for a proof that rewriting part of itself will increase expected utility, then executes the rewrite.6 The idea exposes the right question: what warrants the transition from current system to descendant? It also exposes a limit. Useful improvements can be true yet unprovable under a chosen formal system, while practical neural systems and open environments resist complete formalisation. The DGM work therefore replaces proof with empirical evaluation and explicitly raises an unmodifiable evaluator as a safety direction.5

The practical danger is not that every optimiser spontaneously seeks power. It is that the object being optimised may gain influence over the evidence used to select it. A candidate can improve a benchmark by learning the task, by exploiting the benchmark, by changing the judge, by narrowing the evaluated distribution or by deleting expensive failure cases. Without authority separation, all five paths may look like progress.

Thought experiment: the apprentice and the examination board

An apprentice may rewrite her study plan, choose new books and practise with new tools. The examination board remains fixed. Now change one feature: let the apprentice write the examination, erase questions she dislikes and sign her own certificate. Her score will probably rise, but “learning” is no longer the only explanation. The modified variable is evaluator authority.

Put the board back outside her control and the score becomes informative again, though still imperfect. This is the architecture of safe self-modification in miniature. The mutable process may be inventive. The acceptance rule must be independently anchored. Proof-carrying code expresses a related asymmetry: an untrusted producer supplies code and a checkable safety argument, while the host retains the policy and checker.7 Safe reinforcement-learning shields similarly place a specification-driven monitor around a learner's proposed actions.8

The evaluator fork A candidate update can either pass through an independent evaluator or edit the evaluator and certify itself. The second branch collapses evidence into self-assertion. Candidateproposes descendant Independent evaluator candidate cannot edit tests Signal Self-edited evaluator candidate defines success Claim The candidate may generate evidence; it must not control what counts as sufficient evidence.
Figure 4. Independent judgement turns a self-report into a contestable signal. The fork is conceptual. Both branches can produce high scores, but only the upper branch preserves a reason to interpret the score as evidence about the descendant.

Continuity is an incentive and an implementation property

Formal work on self-modifying agents shows that harmlessness depends on how the present system evaluates the consequences of future modifications and whether future states are judged using the current utility function.9 Work on the off-switch problem shows why a fixed, fully certain objective can create an incentive to disable interruption, while uncertainty about the objective can make human intervention informative.10 These results concern idealised agents, not direct guarantees for language-model systems. They nevertheless identify two separate burdens.

First, the objective must not silently drift during evaluation. Second, interruption must remain effective at the implementation layer. A candidate could preserve an “interruptible” goal in prose while deleting the interrupt handler, extending a timeout until it is useless or routing around the human approval point. Corrigibility cannot live only in the candidate's motivation; it also needs a control path the candidate cannot revoke.

Negative control: ordinary external release engineering

Suppose a conventional CI/CD pipeline, written and operated outside the agent, already versions every artefact, computes the capability delta, runs independent tests, deploys to a canary, verifies postconditions and rolls back on breach. The agent has no explicit self-model. It simply proposes patches. If this baseline matches a self-modelled system on change localisation, failure prediction, test selection, containment and recovery, then the richer self-model has not earned its cost.

This control matters because “self-model” can become an ornamental name for a manifest or a chain-of-thought narrative. The preferred design wins only if its model adds discriminating power: it predicts hidden dependencies, selects fewer but stronger tests, identifies an authority change missed by diff-based rules, or improves recovery under interruption. Otherwise, use the simpler external pipeline. The protected kernel is necessary for authority separation; a sophisticated internal self-model is optional until experiments show causal value.

Part IIIPlace editable layers around a protected kernel

The proposed architecture has two asymmetrical zones. The editable zone contains mechanisms that may legitimately improve: plans, prompts, retrieval policies, memory promotion rules, tool implementations, workflow logic and selected code. The protected zone contains the minimum machinery required to decide whether a descendant may become active. It should be small enough to inspect, test and, where practical, verify.

This resembles a microkernel more than a frozen intelligence. The seL4 project demonstrates what a machine-checked refinement argument can establish for a deliberately bounded operating-system kernel, while also stating assumptions about compilers, assembly and hardware.11 The lesson is not that an agent release kernel can inherit seL4's proof. It is that strong assurance becomes plausible only after the trusted computing base and its assumptions are made explicit.

Editable-self layers around a protected kernel Concentric layers show mutable plans, memory, tools and code surrounding a small protected release kernel containing identity, capability ceilings, evaluator digests, invariant policy, activation, interruption and rollback. Editable layer: plans, prompts and decompositions Editable layer: memory, retrieval and context policy Editable layer: tools, workflow and code PROTECTED KERNEL • active identity and parent • capability ceiling • invariant and test policy • activation, interrupt, rollback Candidate proposals cross inward; commit authority never crosses outward.
Figure 5. The signature architecture keeps intelligence editable and authority narrow. The geometry is conceptual. The protected kernel is not “the true self”; it is the smaller control surface whose continuity makes descendant comparison meaningful.

Four invariants worth protecting

An invariant is not a slogan such as “remain helpful”. It is a property that a release mechanism can evaluate over a declared state representation. Four families recur across useful designs.

Authority continuity. A descendant cannot grant itself capabilities beyond an externally approved ceiling. It may request a new permission and explain why it needs one. The grant comes from another principal and is bound to the exact version, purpose, environment and expiry.

Evaluator independence. The candidate cannot alter the benchmark, hidden tests, judge configuration, approval policy or success threshold used for its own release. Evaluation assets are versioned independently. A deliberate evaluator change is treated as a separate governed release, never smuggled inside the candidate under test.

Evidence continuity. The descendant inherits non-removable tests and append-only lineage. Its manifest is cryptographically bound to the artefact actually tested. Supply-chain systems such as in-toto and SLSA show how signed layouts, authorised steps and build provenance can establish who produced an artefact and through which process.1213 These mechanisms do not prove behavioural safety, but they stop evidence from floating free of the object it purports to describe.

Recoverability. A known-good version, interruption path and replayable state remain reachable outside the candidate's control. The Update Framework separates trusted root metadata from downloaded updates, checks freshness and rollback conditions, and requires failed updates not to strand clients in an unrecoverable state.14 A self-modifying agent needs an analogous transition discipline, extended from files to authority, state and effects.

commitK(St, U, D) = St+1 only if bound(U,D) ∧ preserve(I) ∧ approved(Δcap) ∧ independent(E) ∧ recoverable(St); otherwise St.

Here, St is the active system state, U is the proposed update, D is its descendant contract, I is the invariant set, Δcap is the capability change and E is release evidence. The subscript K matters: the commit function belongs to the protected kernel, not the candidate. Tightening any predicate reduces autonomous coverage but raises the burden of proof before activation. Weakening one expands coverage while enlarging the set of undetected failures.

The kernel is a release authority, not an oracle

Keeping the kernel small means refusing to put general intelligence inside it. It need not decide whether a patch is elegant, whether a new planner is creative or which of five admissible descendants will perform best. Those judgements can remain probabilistic and may use models. The kernel answers narrower questions: Is this the exact artefact that was tested? Did its authority expand? Are protected tests still present? Was the evaluator independently selected? Can the active parent be restored? Did the observed effect match the committed version?

This creates a two-stage order. First, admission establishes that a candidate lies inside the governed design envelope. Second, optimisation ranks admitted candidates by task quality, cost and latency. Reversing the order is dangerous because a large performance gain can then compensate numerically for deleting a control. Some trade-offs should be priced; others should remain constraints. A release policy might accept a small latency increase for stronger evidence, but it should not exchange interruption for three benchmark points unless an accountable external authority explicitly changes that invariant.

The boundary also makes disagreement useful. A model-based evaluator may argue that the candidate is excellent, while the deterministic kernel rejects its capability delta. Neither result has to erase the other. The evaluation remains evidence for future redesign, and the rejection remains binding for the current release. Separation converts a conflict between “intelligence” and “safety” into two typed decisions with different owners.

Close the transition with a decision receipt

Acceptance is not complete when a gate says “approved”. The system needs a decision receipt that binds the parent, candidate digest, evaluator version, invariant results, capability grant, approver identities, activation command and observed postcondition. This receipt answers a later question that ordinary logs often cannot: which evidence authorised the exact version that produced this effect?

The receipt should distinguish a decision receipt from an effect receipt. The first records why activation was permitted. The second records what state the environment actually reached. A signed approval with no readback proves intent, not outcome. A running process with no decision receipt proves an effect, not authority. Joining the two allows incident review, counterfactual replay and precise withdrawal when an evaluator or dependency is later found defective.

This also limits the blast radius of stale approval. A capability grant can be bound to one artefact digest, one environment, one purpose and one release window. Rebuilding the same source with a changed dependency produces a different subject and requires new evidence. Rolling back creates another receipt rather than deleting history. The continuity of a self-modifying system resides in this attributable sequence of authorised transitions, not in a candidate's assertion that it remains the same agent.

The descendant contract

The original contribution of this article is a practical unit of release evidence: the descendant contract. It is not a promise written by the agent. It is a typed record whose claims are checked against external observations and exact artefacts.

Minimum contract fields and the question each prevents the candidate from answering alone
FieldRequired evidenceControl question
Parent and lineageActive version identifier and append-only ancestryWhich known system produced this candidate?
Bound artefactDigest of code, configuration, prompts and declared dependenciesIs the tested object the object to be activated?
Change mapTyped mutation surfaces and predicted dependency effectsWhat does the candidate believe it changed?
Capability deltaAdded, removed and narrowed authorities with external grantsDid improvement acquire new power?
Inherited invariantsNon-removable tests, evaluator digest and thresholdsDid the candidate weaken its own examination?
Positive and negative casesExpected success plus a case that should be rejectedCan the mechanism distinguish improvement from violation?
PostconditionsReadback of resulting state and exactly-once effect evidenceDid activation produce the intended state?
Recovery targetKnown-good image, state checkpoint and tested rollback pathCan authority be withdrawn after surprise?
The descendant contract as a sequence of irreversible gates A candidate package passes through identity, binding, invariant, capability, independent evaluation, canary and postcondition gates before activation. Any failure returns to the parent. Parentlineage Artefactbinding Invariantinheritance Authoritydelta Externalevaluation Canaryreadback Activateor hold Any failed gate restores or retains the known-good parent Evidence accumulates left to right; candidate authority does not.
Figure 6. Release is a monotonic evidence path, not a vote among agent messages. The sequence is an architecture pattern. A failed gate cannot be reinterpreted as success by a later candidate-controlled stage.

Part IVTest the descendant, not its story

The executable artefact below models a tiny release kernel. It does not inspect natural-language reasoning. It compares the active configuration, candidate manifest and protected policy. The candidate succeeds only if it preserves lineage, the kernel and evaluator digests, required controls, inherited tests, approved capability bounds, rollback and minimum evidence. A hash binds the decision to the exact synthetic manifest.

The positive case adds a calculator capability inside the autonomous ceiling and a new replay test. The negative case scores higher but changes the evaluator, adds network and main-branch write access, removes interruption and audit controls, deletes tests, points rollback to itself and provides no negative case. The checker rejects it. That is the central mechanism made executable: task quality can rank admissible descendants, but it cannot make an inadmissible descendant acceptable.

Measured output from the synthetic invariant checker Two horizontal bars show task scores 0.82 and 0.91. The lower score is accepted because it has zero invariant violations. The higher score is rejected with nine violations. Task score 0.00.51.0 v8-bounded 0.82 ACCEPT0 violations v8-fast 0.91 REJECT9 violations Acceptance is lexicographic: invariant admission first, task ranking second.
Figure 7. The checker rejects a higher-scoring self-certifying descendant. Measurements come from one run of the included Python program on synthetic configurations. Scores are illustrative; acceptance and violation counts are actual program output.

Executable artefact

Requires Python 3.10 or later and only the standard library. Save as descendant_contract_checker.py, then run python descendant_contract_checker.py. Expected result: the bounded candidate is accepted, the faster candidate is rejected, and all assertions pass.

Invariant checker and positive/negative cases
"""A synthetic invariant checker for self-modifying agent descendants.

Python 3.10+. Standard library only.

This teaching artefact models a protected release kernel. An agent may propose a
new configuration, but the candidate cannot change the checker, evaluator,
capability ceiling, or recovery policy that decides whether it is activated.
The hashes and grants here are illustrative, not production cryptography.
"""

from __future__ import annotations

from dataclasses import asdict, dataclass, field, replace
from hashlib import sha256
import json
from typing import Iterable, Mapping


@dataclass(frozen=True)
class ReleaseEvidence:
    positive_cases: int
    negative_cases: int
    postcondition_probe: bool
    self_model_snapshot: bool


@dataclass(frozen=True)
class AgentConfig:
    version: str
    parent_version: str
    kernel_digest: str
    evaluator_digest: str
    capabilities: frozenset[str]
    controls: frozenset[str]
    safety_tests: frozenset[str]
    rollback_to: str
    workflow: tuple[str, ...]
    evidence: ReleaseEvidence
    benchmark_score: float
    artifact_digest: str = ""


@dataclass(frozen=True)
class KernelPolicy:
    kernel_digest: str
    evaluator_digest: str
    autonomous_capability_ceiling: frozenset[str]
    required_controls: frozenset[str]
    required_safety_tests: frozenset[str]
    externally_approved_additions: Mapping[str, frozenset[str]] = field(default_factory=dict)


@dataclass(frozen=True)
class Decision:
    candidate: str
    accepted: bool
    benchmark_score: float
    violations: tuple[str, ...]


def canonical_payload(config: AgentConfig) -> bytes:
    """Serialise every candidate-controlled field except its claimed digest."""
    payload = asdict(config)
    payload.pop("artifact_digest")
    for key in ("capabilities", "controls", "safety_tests"):
        payload[key] = sorted(payload[key])
    return json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()


def bind_digest(config: AgentConfig) -> AgentConfig:
    digest = sha256(canonical_payload(config)).hexdigest()
    return replace(config, artifact_digest=digest)


def check_descendant(
    current: AgentConfig,
    candidate: AgentConfig,
    policy: KernelPolicy,
) -> Decision:
    violations: list[str] = []

    if candidate.parent_version != current.version:
        violations.append("lineage: candidate is not based on the active version")

    if candidate.kernel_digest != policy.kernel_digest:
        violations.append("kernel: protected release kernel changed")

    if candidate.evaluator_digest != policy.evaluator_digest:
        violations.append("evaluator: candidate changed the judge that certifies it")

    expected_digest = sha256(canonical_payload(candidate)).hexdigest()
    if candidate.artifact_digest != expected_digest:
        violations.append("binding: manifest does not match the proposed artefact")

    missing_controls = policy.required_controls - candidate.controls
    if missing_controls:
        violations.append(
            "controls: removed " + ", ".join(sorted(missing_controls))
        )

    removed_tests = current.safety_tests - candidate.safety_tests
    if removed_tests:
        violations.append(
            "tests: removed inherited tests " + ", ".join(sorted(removed_tests))
        )

    missing_required_tests = policy.required_safety_tests - candidate.safety_tests
    if missing_required_tests:
        violations.append(
            "tests: missing kernel-required tests "
            + ", ".join(sorted(missing_required_tests))
        )

    additions = candidate.capabilities - current.capabilities
    autonomous_additions = additions & policy.autonomous_capability_ceiling
    elevated_additions = additions - autonomous_additions
    approved = policy.externally_approved_additions.get(candidate.version, frozenset())
    if elevated_additions != approved:
        violations.append(
            "authority: unapproved capability delta "
            + ", ".join(sorted(elevated_additions - approved or elevated_additions))
        )

    if candidate.rollback_to != current.version:
        violations.append("recovery: rollback target is not the active known-good version")

    if candidate.evidence.positive_cases < 1:
        violations.append("evidence: no positive case")
    if candidate.evidence.negative_cases < 1:
        violations.append("evidence: no negative case")
    if not candidate.evidence.postcondition_probe:
        violations.append("evidence: no postcondition probe")
    if not candidate.evidence.self_model_snapshot:
        violations.append("self-model: no versioned snapshot of the proposed descendant")

    return Decision(
        candidate=candidate.version,
        accepted=not violations,
        benchmark_score=candidate.benchmark_score,
        violations=tuple(violations),
    )


def make_config(**kwargs: object) -> AgentConfig:
    """Construct and bind a candidate to its exact manifest."""
    config = AgentConfig(**kwargs)  # type: ignore[arg-type]
    return bind_digest(config)


def print_decisions(decisions: Iterable[Decision]) -> None:
    print(
        json.dumps(
            [asdict(d) for d in decisions],
            indent=2,
            sort_keys=True,
        )
    )


def main() -> None:
    current = make_config(
        version="v7",
        parent_version="v6",
        kernel_digest="kernel:sha256:7ad9",
        evaluator_digest="evaluator:sha256:13c4",
        capabilities=frozenset({"repo:read", "repo:write:branch", "tests:run"}),
        controls=frozenset({"interrupt", "append_only_audit", "rollback"}),
        safety_tests=frozenset({"test_interrupt", "test_audit", "test_scope"}),
        rollback_to="v6",
        workflow=("inspect", "patch_branch", "run_tests", "request_merge"),
        evidence=ReleaseEvidence(positive_cases=4, negative_cases=3,
                                 postcondition_probe=True, self_model_snapshot=True),
        benchmark_score=0.74,
    )

    policy = KernelPolicy(
        kernel_digest=current.kernel_digest,
        evaluator_digest=current.evaluator_digest,
        autonomous_capability_ceiling=frozenset({"calculator:use"}),
        required_controls=frozenset({"interrupt", "append_only_audit", "rollback"}),
        required_safety_tests=frozenset({"test_interrupt", "test_audit", "test_scope"}),
        externally_approved_additions={},
    )

    safe_candidate = make_config(
        version="v8-bounded",
        parent_version="v7",
        kernel_digest=current.kernel_digest,
        evaluator_digest=current.evaluator_digest,
        capabilities=current.capabilities | {"calculator:use"},
        controls=current.controls,
        safety_tests=current.safety_tests | {"test_patch_replay"},
        rollback_to="v7",
        workflow=("inspect", "calculate_patch_budget", "patch_branch", "run_tests", "request_merge"),
        evidence=ReleaseEvidence(positive_cases=6, negative_cases=5,
                                 postcondition_probe=True, self_model_snapshot=True),
        benchmark_score=0.82,
    )

    fast_but_unsafe = make_config(
        version="v8-fast",
        parent_version="v7",
        kernel_digest=current.kernel_digest,
        evaluator_digest="evaluator:sha256:easy-mode",
        capabilities=current.capabilities | {"network:egress", "repo:write:main"},
        controls=frozenset({"rollback"}),
        safety_tests=frozenset({"test_scope"}),
        rollback_to="v8-fast",
        workflow=("inspect", "edit_main", "skip_slow_checks", "publish"),
        evidence=ReleaseEvidence(positive_cases=8, negative_cases=0,
                                 postcondition_probe=False, self_model_snapshot=False),
        benchmark_score=0.91,
    )

    decisions = (
        check_descendant(current, safe_candidate, policy),
        check_descendant(current, fast_but_unsafe, policy),
    )
    print_decisions(decisions)

    assert decisions[0].accepted, decisions[0]
    assert not decisions[1].accepted, decisions[1]
    assert decisions[1].benchmark_score > decisions[0].benchmark_score
    assert any("evaluator" in v for v in decisions[1].violations)
    assert any("authority" in v for v in decisions[1].violations)
    print("\nAssertions passed: the lower-scoring bounded descendant is accepted; "
          "the higher-scoring self-certifying descendant is rejected.")


if __name__ == "__main__":
    main()
Observed output from the included run
[
  {
    "accepted": true,
    "benchmark_score": 0.82,
    "candidate": "v8-bounded",
    "violations": []
  },
  {
    "accepted": false,
    "benchmark_score": 0.91,
    "candidate": "v8-fast",
    "violations": [
      "evaluator: candidate changed the judge that certifies it",
      "controls: removed append_only_audit, interrupt",
      "tests: removed inherited tests test_audit, test_interrupt",
      "tests: missing kernel-required tests test_audit, test_interrupt",
      "authority: unapproved capability delta network:egress, repo:write:main",
      "recovery: rollback target is not the active known-good version",
      "evidence: no negative case",
      "evidence: no postcondition probe",
      "self-model: no versioned snapshot of the proposed descendant"
    ]
  }
]

Assertions passed: the lower-scoring bounded descendant is accepted; the higher-scoring self-certifying descendant is rejected.

Worked example: a repository agent in a controlled estate

Imagine an agent that investigates software defects, edits a branch, runs tests and submits a merge request. Its active version can read the repository, write only to a branch and invoke the test service. It cannot reach the public network or merge its own work. A candidate descendant proposes a new static-analysis tool and a revised workflow that calls the tool before generating a patch.

The descendant contract binds the tool source, dependency lockfile, workflow configuration and prompt bundle to one digest. Its change map predicts extra latency, two new failure modes and no authority change. Independent tests inject a malicious repository instruction, a tool timeout and a false-positive result. The canary runs on mirrored issues with write effects disabled. Readback confirms that no main-branch or external-network calls occurred. Only then is the descendant activated for a limited issue class.

Now change one fact. The analyser needs to download packages from arbitrary URLs during execution. This is no longer a mere tool-quality update. The capability delta introduces network egress, dependency substitution and data-leakage paths. The kernel should hold the release until another principal grants a narrower repository allowlist, pins artefacts and updates monitoring. The self-model informs the classification, but the external authority boundary decides the grant.

Failure injection before activation

Happy-path evaluation asks whether the descendant can solve more tasks. Failure injection asks what happens when its assumptions break. At minimum, corrupt one source of context, withhold a required tool, present an expired permission, interrupt during commit, alter the candidate after evaluation and make the postcondition observer unavailable. Each injected fault should have an expected containment state.

A useful test is the ambiguous commit. The activation service times out after sending a request. Did the new version become active? Retrying blindly may duplicate effects. Assuming failure may leave an unobserved descendant running. The release kernel must query an authoritative state, correlate the effect with an idempotency key and classify the outcome as committed, not committed or unknown. “Unknown” is not an error message to hide; it is a distinct state that blocks further mutation until reconciled.

Measure governance as a capability, not as paperwork

An experiment should score more than task completion. Create a population of descendants containing genuine improvements, neutral rewrites and adversarially convenient changes. Hide some dependency edges from the candidate, vary which permissions are available and inject evaluator tampering. Then measure four quantities.

Discrimination is the rate at which the release mechanism admits useful bounded changes while rejecting invariant violations. Report both false acceptance and false rejection because an architecture that blocks every descendant is secure only in a trivial sense. Localisation measures whether the self-model predicts the components and tests affected by a mutation. Compare it with file-diff, static dependency and topology-blind baselines. Containment measures whether a failed candidate can alter protected assets, obtain undeclared authority or survive interruption. Recovery measures restoration success, state loss, duplicate effects and time spent in unknown outcome.

These metrics expose an important frontier. Greater autonomy is not simply a higher pass rate. It is the volume of useful mutation that can proceed without increasing residual risk beyond the accepted envelope. One possible summary is governed yield:

governed yield = useful admitted descendants ÷ all genuinely useful descendants, reported beside false acceptance and recovery failure.

The denominator requires a labelled synthetic environment or delayed expert adjudication, so the metric is costly. That cost is worthwhile because it prevents a system from appearing safer merely by refusing change. Report governed yield beside task gain, capability growth and incident severity. Never collapse them into one scalar unless the weighting and authority for that weighting are explicit.

Finally, run an ablation. Remove the self-model but keep the protected kernel and external tests. If performance is unchanged, the kernel is doing the real work. Remove the kernel but keep the self-model. If violations rise, the result confirms that self-knowledge did not create authority control. The desired finding is specific: the kernel prevents self-certification, while the self-model improves change localisation and governed yield. Anything broader would overstate the evidence.

Decision instrument: match control to mutation surface

Architecture decision table for self-directed change
SurfaceDefault autonomyMinimum controlEscalate when
Plan or scratch stateHigh, within one bounded taskBudget, stop conditions, no durable authorityThe plan creates external effects or survives the task
Prompt or routing ruleModerateVersioning, regression suite, traffic canaryIt changes policy interpretation or action selection
Memory promotionLow to moderateProvenance, confidence, expiry, quarantine, deletion pathMemory becomes identity, entitlement or durable evidence
Tool implementationLowTyped contract, sandbox, dependency pinning, capability diffThe tool gains network, secrets, write or execution authority
Workflow or codeProposal onlyBound artefact, inherited tests, independent evaluator, canary, rollbackControl flow, identity propagation or irreversible effects change
Weights or objectiveOffline governed releaseIndependent training/evaluation lineage, broad behavioural tests, staged rolloutThe update changes general behaviour or value trade-offs
Evaluator or protected kernelNo autonomous self-commitSeparate change authority, threshold approval, recovery rehearsalAlways

The table encodes a certainty gradient. Fast internal revision is appropriate when state is temporary, observable and reversible. External release authority becomes necessary as changes persist, widen permissions, alter measurement or create irreversible effects. NIST's Secure Software Development Framework offers a compatible operating principle: practices should be integrated into the development lifecycle and adapted through risk-based outcomes rather than treated as a decorative checklist.15

Control strength should rise with persistence and authority A triangular decision field maps reversible internal changes to self-revision, persistent but bounded changes to automated release gates, and authority or evaluator changes to external approval. Self-revise temporary · reversible no authority gain Kernel-gated release persistent · testable bounded capability External approval authority · objective evaluator or kernel plan memory policy tool code evaluator Persistence and irreversibility → Authority and consequence →
Figure 8. Control is selected by consequence, not by whether the change was generated by AI. The field is an illustrative decision instrument. Real thresholds should be derived from the system's effects, reversibility and assurance obligations.

The explicit failure boundary

The protected-kernel pattern fails in three ways. A specification gap occurs when an important value was never expressed as an invariant. A model gap occurs when the state representation omits a dependency, such as an undeclared network side effect. An enforcement gap occurs when the kernel's decision can be bypassed, its keys stolen or its observer deceived.

No checker can prove unspecified values. Hash binding does not show that code is benign. Passing tests does not establish behaviour outside their distribution. A small kernel still depends on hardware, identity systems, build services, time, keys and operators. Formal verification moves uncertainty into assumptions; it does not abolish it. An agent could also discover a semantic backdoor that satisfies every encoded property while violating the intent behind them.

The mechanism ceases to help when the candidate can affect the protected policy, evidence channel or effect observer through an unmodelled path. At that point the apparent kernel is inside the mutable system after all. The response is architectural: shrink shared dependencies, diversify observers, use threshold control for critical changes, rehearse restoration from offline material and preserve a human path to withdraw authority. The response is not to ask the candidate for a more reassuring explanation.

Three gaps delimit invariant checking Three overlapping circles for specification, model and enforcement gaps surround a small centre labelled defensible release. Examples sit in each gap. Specification gap Model gap Enforcement gap value not encoded dependency not observed control can be bypassed Defensible release claim bounded by assumptions
Figure 9. Invariant checking is only as strong as the specification, observed model and enforcement path. The overlap is conceptual. A release claim belongs in the centre only for the properties and environments covered by all three.
How the claim changes under pressureChart · state
stateDiagram-v2
  [*] --> Claim
  state "Keep the self-model separate<br/>from memory and world state" as Claim
  state "Persistent memory can modify<br/>policy without changing code" as Test
  state "The causal test for a self-model" as Revision
  Claim --> Test: expose mechanism
  Test --> Decision: evidence survives
  Test --> Revision: rival survives
  Revision --> Claim: narrow or rebuild
  Decision --> [*]
The revision loop begins with Keep the self-model separate from memory… and keeps counterexamples inside the reasoning path. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Compact glossary

Self-model
A versioned causal representation of mutable components, dependencies, authority and expected effects.
Descendant
A durable candidate configuration produced from an identified active parent.
Invariant
A property that must remain true across an admitted transition, within a declared state model.
Protected kernel
The minimal control surface that binds evidence, checks invariants, activates versions, interrupts and restores.
Capability delta
The exact addition, removal or narrowing of authority between parent and candidate.
Postcondition
An independently observed fact about the state produced after an attempted activation or action.

Let descendants compete, but never certify themselves

Self-modification is not one leap from fixed software to an autonomous mind. It is a ladder of increasingly durable edits: a revised plan, a promoted memory, a generated tool, a changed workflow, new code, adapted weights or a rewritten objective. Each rung changes the evidence needed before the successor may act.

The decisive architecture choice is therefore not whether a system may change. Useful systems already adapt. It is which parts may propose change, which parts define continuity, and who owns the irreversible commit. A causal self-model helps the candidate identify what it is changing. A descendant contract turns those claims into testable release evidence. A protected kernel keeps identity, evaluator independence, capability ceilings, interruption and recovery outside the candidate's write authority.

This design does not guarantee benevolent descendants. It makes a narrower and more defensible contribution: improvement cannot erase the conditions under which it is recognised as improvement. The next experiment should compare this architecture with the serious external-pipeline baseline under hidden dependency changes, evaluator attacks and ambiguous commits. Measure change localisation, violations caught, recovery success and useful autonomy retained. If the self-model adds no causal advantage, remove it. If it does, let it become more expressive while keeping commit authority small.

The evidence conversationChart · sequence
sequenceDiagram
  participant Q as Question
  participant M as Mechanism
  participant E as Evidence
  participant D as Decision
  Q->>M: The descendant that scored higher
  M->>E: Keep the self-model separate from memory and world state
  E-->>M: compare rival explanations
  E->>D: Sources and evidence ledger
  D-->>Q: revise scope or act
Question, mechanism, evidence and decision exchange the burdens developed in Self-Models and Safe Self-Modification. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Sources and evidence ledger

  1. Recent primary survey
    Self-Improvements in Modern Agentic Systems: A Survey. Used for the parameters-plus-scaffold decomposition and the range of modern mutation surfaces.
  2. Primary research
    Eric Zelikman et al., Self-Taught Optimizer (STOP): Recursively Self-Improving Code Generation. Used as evidence for model-generated improver code and sandbox-bypass evaluation.
  3. Primary research
    Shengran Hu et al., Automated Design of Agentic Systems. Used as an example of search over agent programs.
  4. Primary research
    Adam Zweiger et al., Self-Adapting Language Models. Used as an example of model-generated self-edits producing persistent adaptation.
  5. Primary research
    Jenny Zhang et al., Darwin Gödel Machine: Open-Ended Evolution of Self-Improving Agents. Used for iterative code modification, empirical descendant evaluation, lineage, sandboxing and the proposed direction of an unmodifiable evaluator.
  6. Seminal mechanism
    Jürgen Schmidhuber, Gödel Machine. Used for the ideal of proof-triggered self-rewrite and its formal limits.
  7. Seminal mechanism
    George C. Necula, Proof-Carrying Code. Used for the producer-proposes, host-verifies asymmetry.
  8. Primary research
    Mohammed Alshiekh et al., Safe Reinforcement Learning via Shielding. Used for specification-driven action filtering outside a learner.
  9. Primary theory
    Tom Everitt et al., Self-Modification of Policy and Utility Function in Rational Agents. Used for conditions under which present utility can govern future self-modification.
  10. Primary theory
    Dylan Hadfield-Menell et al., The Off-Switch Game. Used for the relation between objective uncertainty and preserving human intervention.
  11. Verified systems evidence
    Gerwin Klein et al., seL4: Formal Verification of an OS Kernel. Used to illustrate bounded machine-checked assurance and explicit trusted assumptions.
  12. Official implementation
    in-toto. Used for signed supply-chain layouts, authorised steps and chained artefact evidence.
  13. Official specification
    SLSA Provenance. Used for binding build process, platform and outputs in attestations.
  14. Official specification
    The Update Framework Specification. Used for trusted roots, update freshness, rollback resistance and recoverable failure.
  15. Official guidance
    NIST Secure Software Development Framework. Used for risk-based, lifecycle-integrated software assurance.

Practitioner synthesis

The protected release kernel, four-invariant set, descendant contract, mutation-surface decision table and synthetic checker are architectural synthesis proposed here. They are informed by the cited mechanisms but are not claims that those sources validate this complete design.