Why Dynamic AI Systems Break in Two Different Directions

Agents
Systems
Distributed Systems
Why extensible AI runtimes need both reversible changes and reactive dependency lifecycles.
Author

Chirag Sehra

Published

August 17, 2026

Modern AI systems are becoming less like single programs and more like small operating environments.

An agent may have an LLM provider, tool registry, MCP servers, filesystem layer, sandbox, memory backend, session store and an agent loop. More importantly, these pieces may not remain fixed for the entire lifetime of the process.

An MCP server can disappear.

A model adapter can be replaced.

A new tool can be installed.

A memory backend can reconnect.

An agent may even modify the software environment in which it runs.

DeepSeek Harness makes this architectural problem concrete: it uses Cordis as the plugin framework, with services and capabilities composed through a shared context rather than a permanently privileged monolithic core.(DeepSeek AI 2026)

The programming-language paper behind Cordis, A Programming Paradigm for Spatiotemporal Composability, asks a deeper question:

What properties would a programming model need if components could be added, removed and replaced while the program stays alive?

The paper identifies two orthogonal problems:

It addresses these using revertible effects and reactive coeffects, then combines both into a component lifecycle model and develops system-level results for dynamic composition.(Cordiverse 2026)

This series explains those ideas for engineers building LLM systems, agent runtimes and extensible infrastructure.

We will use the same small Python runtime throughout:

Agent Runtime
│
├── LLM Provider
├── Tool Registry
├── MCP Plugin
├── Memory
├── Sandbox
└── Agent Loop

The Python code is pedagogical. It is not a Python port of Cordis.

Each part also uses operating-system concepts as comparison points. These analogies are useful because most engineers already understand process lifetime, resource ownership, device dependencies and lifecycle races. However, the analogies are not equivalences. Wherever an OS comparison stops matching the paper, we will say so explicitly.

The two dimensions at a glance

The shortest mental model is: temporal composability asks whether a component leaves residue; spatial composability asks whether it should be running at all.

flowchart LR
  T["Temporal: what did I change?"] --> U["Own the effect lifetime"]
  S["Spatial: what do I need?"] --> V["React to dependency changes"]
  U --> W["Composable component"]
  V --> W


Installing a Plugin Is Easy. Removing It Correctly Is Not.

Suppose you are building a small agent runtime.

The first version may look completely reasonable:

class Runtime:
    def __init__(self):
        self.services = {}
        self.tools = {}
        self.hooks = []

    def register_service(self, name, service):
        self.services[name] = service

    def register_tool(self, name, tool):
        self.tools[name] = tool

    def register_hook(self, hook):
        self.hooks.append(hook)

Now we add an LLM provider:

class DeepSeekProvider:
    async def complete(self, messages):
        ...

and install it:

runtime.register_service("llm", DeepSeekProvider())

Then we add a search plugin:

def install_search_plugin(runtime):
    runtime.register_tool("web_search", web_search)
    runtime.register_hook(log_search_result)

Everything works.

The agent can call the model and use search.

The interesting problem begins when we ask:

uninstall_search_plugin(runtime)

What exactly should that operation do?

It has to remove:

web_search from the tool registry
log_search_result from the hook registry
any timers created by the plugin
any event listeners created by the plugin
any child components started by the plugin
any services exposed by the plugin

If the plugin changed ten runtime structures, its cleanup path needs to remember all ten.

That is already uncomfortable.

Now add another requirement:

Replace the LLM provider without restarting the agent process.

Our runtime currently has:

agent_loop.llm = runtime.services["llm"]

Suppose the agent loop stores that reference during startup.

Then we replace:

runtime.services["llm"] = AnotherProvider()

The runtime says the provider changed.

The agent loop may still be using the old provider.

We now have two different categories of failure.


Problem 1: What Did the Component Change?

Imagine that installing plugin search creates this state:

Before

Tools:
  read_file

Hooks:
  on_message


After search plugin

Tools:
  read_file
  web_search

Hooks:
  on_message
  log_search_result

Removing search should ideally produce exactly:

Tools:
  read_file

Hooks:
  on_message

This is a time problem.

The component existed during a particular interval:

─────────┬──────────────────────────┬─────────>
         load                    unload

During that interval, it modified the environment.

Once its lifetime ends, we want its contribution removed.

The Cordis paper calls this dimension temporal composability: the ability to completely revert a component’s side effects upon removal.(Cordiverse 2026)


Problem 2: What Does the Component Need?

Now consider our agent loop:

class AgentLoop:
    requires = {
        "llm",
        "tools",
        "sessions",
    }

It should not become active when only this exists:

LLM       ✓
Tools     ✓
Sessions  ✗

More importantly, if sessions disappears later, the runtime must react.

This is a space problem.

The component occupies a position in a dependency graph:

          LLM
           │
           ▼
Tools ──► Agent Loop ◄── Sessions

The paper calls this spatial composability: dependencies are declared and reactively managed rather than hidden inside arbitrary startup code.(Cordiverse 2026)


Why These Problems Are Orthogonal

A system can solve one without solving the other.

Perfect cleanup, bad dependencies

Imagine every plugin has perfect undo logic:

dispose = install_plugin()
dispose()

After disposal, nothing leaks.

Great.

But the agent loop still starts before its LLM provider exists.

Temporal composability is good.

Spatial composability is bad.

Perfect dependency injection, bad cleanup

Now imagine a sophisticated dependency-injection framework:

agent = AgentLoop(
    llm=container.resolve("llm"),
    tools=container.resolve("tools")
)

Dependencies are clear.

But unloading AgentLoop leaves behind:

event listeners
registered tools
callbacks
timers
child services

Spatial composition may be structured.

Temporal composition is still broken.

Separating these two dimensions is one of the most useful ideas in the paper.


Example: Breaking an Agent Runtime

Let us intentionally build a bad plugin system.

class Runtime:
    def __init__(self):
        self.services = {}
        self.tools = {}
        self.hooks = []

    def emit(self, event):
        for hook in self.hooks:
            hook(event)

Our search plugin:

class SearchPlugin:
    def load(self, runtime):
        runtime.tools["web_search"] = self.search
        runtime.hooks.append(self.on_event)

    def search(self, query):
        return f"results for {query}"

    def on_event(self, event):
        print("search event:", event)

Install it:

plugin = SearchPlugin()
plugin.load(runtime)

Now suppose we remove our Python reference:

del plugin

Did the plugin disappear?

No.

The runtime still contains references in:

runtime.tools["web_search"]
runtime.hooks

The module’s logical lifetime has ended, but its effects have not.

Dynamic composition needs a stronger relationship between lifetime and state.


Relatable Example: Process Cleanup

Operating systems already teach engineers an important lesson:

Lifetime and resources should be connected.

When a process terminates, the operating system closes resources such as file descriptors associated with that process.(Michael Kerrisk n.d.)

Conceptually:

Process
│
├── fd 3
├── fd 4
└── fd 5

Process exits
     │
     ▼

Kernel releases process-owned descriptors

The process does not need to leave behind an application-level list saying:

please close fd 3
please close fd 4
please close fd 5

Ownership gives the system enough structure to perform some cleanup.

That is a useful intuition for Cordis:

Runtime resources created during a component’s lifetime should be associated with that lifetime.

Limits of the Comparison

Suppose a process does this:

with open("config.txt", "w") as f:
    f.write("new configuration")

When the process exits, the file descriptor is closed.

But the filesystem does not restore the old file contents.

So:

resource release
        ≠
state reversal

The operating system can release resources whose ownership it knows.

The Cordis paper asks for something stronger inside the managed context:

Can context transformations themselves carry enough information to be reversed?

That leads to revertible effects.


Relatable Example: Driver Dependencies

Linux device links provide a useful analogy for the spatial side.

The Linux driver core can represent relationships between a supplier device and a consumer device, including ordering constraints around probing and unbinding.(The Linux kernel developers n.d.)

Conceptually:

Supplier
   │
   ▼
Consumer

Compare this to an AI runtime:

LLM Provider
     │
     ▼
Agent Loop

or:

MCP Transport
     │
     ▼
MCP Tool Adapter

A consumer should not pretend to be active when the capability it requires does not exist.

That is a useful mental model for reactive dependencies.

Limits of the Comparison

Linux device-link machinery solves particular kernel lifecycle problems.

It does not automatically provide the paper’s general reversible-effect model, effect independence or system-level composability results.


Why This Matters More for AI Runtimes

Traditional applications often assume their architecture is fixed between process start and exit.

Agent runtimes increasingly challenge that assumption.

Consider a long-running session:

09:00  Agent starts with Model A
09:07  MCP filesystem server appears
09:14  Search plugin is installed
09:31  Model A is replaced by Model B
09:44  Search plugin is upgraded
10:02  MCP filesystem server disappears
10:08  MCP filesystem server reconnects

Restarting the entire process after every change is possible.

But it gives up dynamic composition.

A runtime that genuinely supports change needs answers to four questions:

1. What did this component change?

2. How do I undo only those changes?

3. What dependencies does this component require?

4. What happens when those dependencies change while it is alive?

The first two belong primarily to the temporal dimension.

The last two belong primarily to the spatial dimension.

The paper’s basic thesis is that both should become part of the programming model.(Cordiverse 2026)


DeepSeek Harness Makes the Problem Concrete

DeepSeek Harness uses Cordis as its runtime composition substrate. The official repository describes the harness as a plugin-based architecture powered by Cordis.(DeepSeek AI 2026)

The mental model is closer to:

             Cordis Context
                   │
      ┌────────────┼─────────────┐
      │            │             │
      ▼            ▼             ▼
    Model         Tools       Sessions
      │            │             │
      └────────────┼─────────────┘
                   ▼
               Agent Loop

than:

       Permanent Agent Core
              │
              ├── optional plugin
              ├── optional plugin
              └── optional plugin

If central capabilities are dynamically composable, lifecycle correctness stops being an edge case.

It becomes architectural.


The Mental Model to Keep

Temporal composability

Component A enters
        │
        ▼
changes shared context
        │
        ▼
Component A leaves
        │
        ▼
A's contribution disappears

Spatial composability

A requires B

B absent
   │
   ▼
A inactive

B appears
   │
   ▼
A activates

B disappears
   │
   ▼
A deactivates

The next four parts make these diagrams progressively more precise.


Next: Revertible Effects: Making Runtime Changes Undoable

References

Cordiverse. 2026. A Programming Paradigm for Spatiotemporal Composability. https://github.com/cordiverse/paper.
DeepSeek AI. 2026. DeepSeek Harness. https://github.com/deepseek-ai/deepseek-harness.
Michael Kerrisk. n.d. _Exit(2): Linux Manual Page. https://man7.org/linux/man-pages/man2/_exit.2.html.
The Linux kernel developers. n.d. Device Links. https://docs.kernel.org/driver-api/device_link.html.