Research / Agents and LLMs

P60 · full · draft

Self-Extending Runtime: A Component Proposes a Component, the Embedded Compiler Decides, Failed Health Reverts Residue-Free

partial full Agents and LLMs

State of the work. Python tier ships Gate.propose with a health-gated swap and named tests; the Rust host cannot swap, re-entrant propose is refused, and no live-model evaluation or performance measurement exists.

This is a draft, not peer reviewed. Repository references link to revl at commit 45bec431f.

Planned to be combined with P59 (A System May Evolve Behaviour, Never the Rules Governing Its Authority: The Admission Kernel as a Capability, and Honest Rollback of Evolved Layers), P61 (Promotion No Measurement Can Buy: Shadow Routing, an Authority Barrier and Verified Canary for Models and Components) into a single paper.

Pitch

A running program can ask a model for a replacement of one of its own components, have its embedded compiler admit it under a profile in which the author cannot introduce host code or choose its own authority address, hot-swap it with state migration, and revert to the previous generation if the successor is not healthy.

Abstract

Hot code upgrade is mature (Erlang/OTP releases, blue-green and canary deployment), and so is automated program repair. Combining them for code written by an untrusted generator raises problems neither literature covers: the swap verb in a runtime assumes its caller already trusted the code, and the admission verb assumes the code is additive. We describe Gate.propose in revl, a verb that sits in the gap. A candidate is compiled standalone under an untrusted-author profile that forbids new host-code declarations, transitive reach to host code, ungranted services and self-chosen realm placement; a granted set naming the decider is refused before compiling; the swap runs through a post-activation health gate that treats any non-clean activation, including the two non-raising ones (a failed fiber, an unmet requirement), as a rejection and reverts to the previous generation. Live state migrates only across declared, typed channels, and a halted session is reported as halted rather than as a revert. Review of the design found that a name-keyed standing approval could be inherited by a successor that reuses a component name, and that a stale handle could dispatch into the successor; both are closed and tested. The Python tier carries a 24-function test file for the core path and 5 for name substitution. We state the open parts: the Rust tier decides but cannot swap, re-entrant propose is refused rather than supported, host code in granted providers stays trusted, and no live-model campaign has been measured.

Problem and claim

In most runtimes, replacing a component is a privileged operation done by a human who reviewed the code. In revl the two relevant verbs partition the space wrongly for self-extension: Session.admit applies the untrusted-author profile but is additive and refuses replacement, while Session.swap replaces a composition but runs the full compiler (docs/design/334-self-extending-runtime.md:148-200). The obvious composition, admit then swap, re-opens the host-code escape the profile exists to close.

A second problem is the failure semantics of the swap. An adversarial review found that a successor that fails to activate (a fiber left FAILED, or a requirement left PENDING) did not trigger the rollback, because activation faults are deliberately non-raising; the runtime would serve a broken generation N+1 with generation N already disposed (docs/design/334-self-extending-runtime.md:34-57). The headline "reverts to generation N" was false for the general fault until a post-activation health gate was added.

Claim: composing standalone untrusted-profile admission, a forbidden-grant refusal, a health-gated swap with typed state migration, and bindings of standing authority to code rather than names yields a self-extension verb whose failure outcomes are all returned as data and all leave generation N serving or explicitly stranded. The claim is about the revertible envelope, not about the safety of granted providers' host code, which the doc says stays trusted.

Contributions

  1. Gate.propose: halt check, forbidden-grant refusal, standalone self-extension compile, health-gated swap, one result object with distinct shapes (HALTED, FORBIDDEN_GRANT, compile refusal with machine-readable why-trace, STATE_UNDISCLOSED, SWAP_REVERTED, swapped with migration) (src/revl/gate.py:522, :906).
  2. The post-activation health gate: any non-clean activation, including non-raising FAILED and PENDING, is a swap rejection routed to the abort path (docs/design/334-self-extending-runtime.md:34-57, src/revl/mcp/session.py:2137).
  3. Authority envelope on proposals: no author-chosen realm placement, and a standing auto-approve rule bound to a per-component signature over body, provides, requires and config, so a successor that reuses a reviewed name does not inherit the review (docs/design/334-self-extending-runtime.md:652-710, src/revl/mcp/session.py:9564, tests/test_334_reviewed_name_substitution.py).
  4. Live-state migration only across declared channels; a handle minted for generation N refuses after the generation moves (docs/design/334-self-extending-runtime.md:830-846).
  5. A bounded evolve loop that turns a refusal into a machine-readable why-trace for a pluggable proposer and retries to a budget (src/revl/evolve_loop.py:209, docs/evolve-loop.md).
  6. An account of design-review findings and their fixes as a case study in what breaks when an untrusted generator drives a hot-swap verb.

Evidence on main

Claimpath:line or testStatusNote
Gate.propose returns distinct data outcomes; refusal is datasrc/revl/gate.py:522-626, :906-1135shipped
Forbidden-grant refusal fires before compile; decider reachable through a requires edge or any service name is also refusedsrc/revl/gate.py:670 (_forbidden_decider_grant), :983; tests test_forbidden_grant_rejects_a_granted_decider_service, test_forbidden_grant_fires_before_compiling, test_forbidden_grant_rejects_a_decider_under_any_service_name, test_forbidden_grant_reaches_the_decider_through_a_requires_edge in tests/test_self_extending_334.py:433-553shippedThe name check inspects the granted set only; the structural check runs on the composed IR against the known crossings host_admit and host_admit_all (_DECIDER_EXTERNS, comment block above it). A decider written as a fresh-named extern is declared out of scope in that comment
Untrusted candidate cannot declare an extern, reach a host extern, or reach an ungranted servicetests/test_self_extending_334.py:357-410shipped
Failed or pending successor reverts to generation Ntest_edge1_failed_activation_reverts_to_gen_N, test_edge1_pending_requirement_reverts_to_gen_N, test_edge1_activation_error_reverts_to_gen_N, test_swap_health_gate_reverts_failed_and_pending_directly (tests/test_self_extending_334.py:229-315)shippedNeeds the cordis-py runtime per the repair-loop doc; not run here
Residue-free revert and post-swap fault reverttest_happy_path_self_extending_loop_reverts_residue_free (:166), test_edge2_post_swap_call_fault_reverts_residue_free (:578), crash tests (:627, :670)shipped
Live state migrates or the swap reverts; state not declared on both sides is refused before teardowntests/test_self_extending_334.py:806-921shippedThe design text for slice 3 says generational migration; the code now passes migrate="declared" (src/revl/gate.py:1090). The paper must follow the code
Name-substitution of a reviewed component does not inherit an auto-approve rulesrc/revl/mcp/session.py:9564 (_component_signature, the content digest the bind uses), tests/test_334_reviewed_name_substitution.py (5 test functions)shipped"three of them fail on the predecessor" is the doc's claim (docs/design/334-self-extending-runtime.md:917); not re-run
Realm self-placement refused on the untrusted-author pathsrc/revl/admit_profile.py:426 (self_extension), check_no_realm_placement at :209, called once at :916shippedOne call site
Stale handle refuses after generation movestests/test_334_propose_handle_binding.py (7 test functions, matching the design note's "seven"; docs/design/334-self-extending-runtime.md:830-846)shippedThe generation check runs before the key check
Structured why-trace to proposer, retry to budgetsrc/revl/evolve_loop.py:209, docs/evolve-loop.md:46-60, tests/test_evolve_loop.py:134,174,187shippedThe proposer is a seam, not a model; tested with a scripted proposer
Bounded self-repair within declared bounds, closed by default, widening stops for a humandocs/repair-loop.md, src/revl/mcp/repair.py, tests/test_repair_loop.py (16 test functions, e.g. :172, :234, :291)shipped (v1)The status rests on the code and tests
Hot-swappable scoring service walked on the real runtimedocs/design/525-webapp-slice3-differentiated.md, tests/test_app_hotswap_725.py (11 test functions)shippedUses Session.swap directly, not Gate.propose; not a propose measurement
Demo: a running system rewrites a component; a broken answer is refused and never queueddemo/self_evolve.py, demo/evolve_bridge.py, tests/test_self_evolution.pyshippedThe demo admits against the running manifest and applies after the call returns; it is the earlier mechanism, not Gate.propose
Rust hostdocs/design/334-self-extending-runtime.md (the 2026-09-14 re-verification)partialRust can decide and fail closed but cannot swap; blocked on the native emitter
Re-entrant proposesame docdesignedRefused by the forbidden-grant rule, not supported

Numbers that exist

Paper outline

  1. Introduction: self-extension, why admit-then-swap is unsafe.
  2. Background: revl admission profiles, session swap, witnessed effects and inverses.
  3. The gap between two verbs; the propose design.
  4. Trust boundary: what is checked, what is not (granted providers' host bodies).
  5. Failure semantics: the health gate, halt dominance, residue and the crash half (write-ahead log).
  6. Authority leakage found in review: realm choice, name substitution, stale handle, unreported state transfer.
  7. The loop: why-trace, budgets, bounded repair.
  8. Evaluation (to run).
  9. Limits and related work.

Threats to validity and limits

Fact-check

Claims in this draft were checked against revl main at commit 45bec431f: 24 checked, 8 corrected.

Cite this draft

@misc{revlp60,
  author       = {Thomas Moussajee},
  title        = {Self-Extending Runtime: A Component Proposes a Component, the Embedded Compiler Decides, Failed Health Reverts Residue-Free},
  year         = {2026},
  howpublished = {\url{https://inso1337.github.io/revl/research/P60.html}},
  note         = {Research draft, not peer reviewed. Written against revl commit 45bec431f. Licensed CC BY 4.0.}
}

Text licensed CC BY 4.0, author Thomas Moussajee.

← All research drafts