The release of DeepSeek Harness (DSH) caused a huge stir in the community: within two days of the main repository going live, its star count quickly surpassed 100,000, making it the fastest project in GitHub history to cross the 100,000-star mark (according to real-time GitHub data as of 2026-08-21, deepseek-ai/deepseek-harness has surpassed 170,000 stars, and plugin repositories tagged with dsh-plugin have exceeded 10,000). Accompanying the DSH release is a paper co-authored by DeepSeek and Peking University—A Programming Paradigm for Spatiotemporal Composability—which mathematically formalizes the design principles of Cordis, the plugin runtime underlying DSH.
DSH's architectural design philosophy can be summarized in a single sentence: Everything is a plugin, and every plugin is reversible. Is this a truly innovative design? Community reactions are far from uniform—some are excited, some are confused, and there are many skeptical voices. The most common objections include:
- "Everything is a plugin" is an unnecessary unification—Agent Loop and Memory are so different; what is the point of unifying them into the same kind of "plugin"?
- "Everything is a plugin" is not DSH's invention. Which Agent framework today is not highly configurable? Isn't Pi Agent also plugin-based?
- What does "reversible plugin" even mean? Side effects are unavoidable—how can they possibly be reversible?
- Why is dynamic plugin updating necessary? Isn't version upgrade sufficient?
The DSH paper actually addresses these questions, but it does so through formalization from the perspective of category theory/functional programming, making it difficult for ordinary developers to penetrate the mathematical notation and grasp its true meaning. The paper repeatedly emphasizes the word "reversible," which prompted some users on Zhihu to @ me, asking whether this concept is similar to the reversible computation theory I have proposed.
This article offers a plain-language explanation of Cordis's design principles from the perspective of reversible computation theory, while also comparing and contrasting it with reversible computation theory. The specific conclusions are:
- Cordis can be viewed as a concrete application of reversible computation theory in the runtime structural space—"plugins" here are not merely functional modules, but delta units that can be rigorously defined mathematically.
- However, fully realizing reversibility requires complementing it with the complete set of practices that reversible computation has already formalized in the compile-time structural space—DSH's concrete implementation partially reflects this (declarative configuration, runtime reconciliation), but the Cordis paper only provides mechanistic descriptions at this layer without formalizing it as a delta algebra.
- "Reversible" in Cordis does not mean running in reverse; it means reversing changes to the runtime structural space—if such changes are viewed as side effects, then Cordis is not managing all side effects, but only a special kind that affects the composite interaction of multiple plugins.
1. Everything Is a Plugin => Everything Is a Delta
The concept of "everything is a plugin" can be expressed as the following formula:
App = Base + Plugin1 + Plugin2 + ...
That is, a large number of features are not built into the base product but provided through plugins. At this level, DSH's approach is similar to Pi Agent—both can be seen as a core providing a minimal feature set, with extensive functionality supplied through extensions.
In reversible computation theory, the above approach corresponds to a concrete instantiation of the following mathematical formula:
Structure = Base + Delta1 + Delta2 + ...
A Plugin can be viewed as a functional increment. This may sound like a truism—incremental development has long been a commonplace notion in software engineering. However, the essential difference is this: in the traditional software engineering context, the above formula is merely a heuristic description, whereas the Cordis paper and reversible computation theory aim to make it a genuine mathematical formula.
This involves the following questions:
- What is the concrete structural form of the basic units participating in the operation—i.e., what exactly is the structure of a Plugin?
- How is this plus sign precisely defined mathematically?
- Are
Base + Plugin1 + Plugin2andBase + Plugin2 + Plugin1equivalent? - In the absence of Base, can
Plugin1 + Plugin2be defined? What is the result of their operation?
The Cordis paper is essentially about constructing a mathematical space and then defining the operational laws among objects within this space to precisely answer the above questions. We will analyze Cordis's detailed formalization later. First, let us consider an interesting question: does "everything is a plugin" include the Base itself?
The Base in Base + Plugin is obviously not a Plugin, so many programmers' initial instinctive reaction is that "everything is a plugin" must be an exaggeration. But from the perspective of reversible computation, this is perfectly natural: A = 0 + A—any total can be viewed as a delta combined with zero. Therefore, everything is a delta is the mathematically precise formulation.
There is a subtle point here: deltas are defined within a structural space that supports delta operations, so "everything is a delta" applies to this already-existing space. Constructing this space requires corresponding work and incurs cost, but this foundational space is not the Base of delta operations. This is analogous to how applications run on top of an operating system—we need to first construct the OS as the foundational space enabling applications to actually run, but we generally do not consider the OS itself to be the Base part of the application system.
In Cordis, everything is a plugin; when no plugins exist, the entire system has no business-visible functionality, corresponding to the zero element in this mathematical space. But this zero does not mean absolute nothingness. This is similar to the vacuum in the physical world. According to modern physics, a vacuum is not truly empty: it is the ground state of quantum fields, containing hidden structures within—zero-point fluctuations, QCD condensates, topological configurations—and possessing non-zero energy density (the cosmological constant). Under extreme conditions, it can even be polarized or "broken down" (Schwinger pair production).
2. Every Plugin Is Reversible => Delta Can Be Positive or Negative
Many people, upon seeing the word "reversible," immediately jump to the first interpretation that comes to mind: running in reverse. Others, approaching from the angle of side effects, reason that plugins executing in a real system inevitably produce numerous side effects—how could these possibly be reversed and undone?
These are common misunderstandings of reversible computation. In fact, "reversible" in reversible computation is neither step-by-step reverse execution, nor compensatory undoing of all side effects. What it refers to is that Delta changes occurring in the structural space can be positive or negative.
Let me first clarify the term "structural space." The software world actually contains two spaces of fundamentally different natures, which together constitute the software's "phase space":
- Compile-time structural space: the space constituted by programs, models, DSLs, and configurations—this is the level of "what the software is"; code is structured objects that are read, merged, and generated at compile time.
- Runtime structural space: the space constituted by process state, memory, dependency graphs, and lifecycles—this is the level of "what the software is doing"; state is created, modified, and destroyed at runtime.
Reversible computation theory explicitly states: reversibility does not require totality—it does not demand reversibility at every level of the entire phase space. The Nop platform's practice demonstrates this: its primary reversibility occurs in the compile-time structural space—all DSLs support delta merging (x-extends) and delta splitting (x-diff); delta operations occur at compile time and model loading time, completely without touching runtime execution traces. DSH, in contrast, has chosen a complementary path: achieving reversibility in the runtime space—explicitly equipping each effect with an inverse function, with the runtime tracking and composing these inverses. With these two spaces distinguished, the statement "reversible refers to Delta changes in the structural space being positive or negative" acquires precise meaning—for DSH, this "structural space" specifically refers to the runtime structural space.
App = Base + (Plugin1, reverse of Plugin1's changes to the runtime structural space) + (Plugin2, reverse of Plugin2's changes to the runtime structural space) + ...
Every plugin, upon activation, produces a paired undo function whose purpose is to reverse the changes the plugin made to the runtime structural space during activation. When the plugin is unloaded, these undo functions are executed in a certain order.
At the conceptual level, this corresponds to:
Structure = Base + Delta1 + Delta2 - Delta2 + Delta3 = Base + Delta1 + Delta3
The Cordis paper proves that after installing plugins 1, 2, and 3, if plugin 2 is unloaded, the result is observationally equivalent to installing plugins 1 and 3 from scratch.
The above may sound abstract; let us look at a concrete DSH plugin example.
// tool-stats.ts —— a session statistics plugin
import { defineTool } from '@deepseek-ai/dsh-tools'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'tool-stats'
export const inject = ['tools'] // Declares dependency: this plugin activates only after the tools service is ready
export function apply(ctx: Context) {
// 1. Register a tool that the model can call
ctx.tools.register(defineTool({
name: 'stat_summary',
description: 'Output invocation statistics for this session',
parameters: {},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute() { return summarize() },
}))
// 2. Listen for LLM request failure events (waterfall mode; must call next() to delegate downstream)
ctx.on('agent/request-error', async (payload, next) => {
countFailure(payload)
return next()
})
// 3. A manually managed resource: heartbeat timer
ctx.effect(() => {
const timer = setInterval(heartbeat, 60_000)
return () => clearInterval(timer) // Paired undo function
})
}
When the plugin is activated (apply is called), it does three things: registers a tool in the tool registry, registers a listener in the event table, and starts a timer. On the surface these appear to be three ordinary registration actions, but the key point is—all three are registered in the form of effects. The mechanism here is: the plugin instance (fiber) exposes its own effect method directly as ctx.effect (they are the same method); registration APIs like ctx.on and ctx.tools.register internally all ultimately resolve to it—the effect body performs the actual registration action and returns the undo function on the spot; the runtime automatically records this effect in the current fiber's disposables list. The author need not manually register anything—each registration, at the moment of completion, hands its undo function to the runtime:
| Registration Action | Internal Call Chain | Paired Undo Function |
|---|---|---|
ctx.on(...) |
register() → ctx.fiber.effect(...) (dsh/vendor/cordis/src/events.ts) |
Removes this listener from the listener array for that event name |
ctx.tools.register(...) |
register() → layers.effect(...) → ctx.effect(...) (dsh/packages/core/tools/src/index.ts, dsh/packages/core/scope/src/store.ts) |
Removes this tool from the current scope's tool layer |
ctx.effect(...) |
Is ctx.fiber.effect(...) (dsh/vendor/cordis/src/fiber.ts) |
The () => clearInterval(timer) written on the spot by the plugin author |
ctx.effectandfiber.effectare the same method—the fiber exposes it as a property of ctx through a mixin. ctx is the interface the plugin receives; fiber is the entity that actually performs registration and cleanup.layers.effectis a domain-level wrapper built on top ofctx.effectfor the tool registry, responsible for "registering to the global layer or the current scope's dedicated layer, automatic reclamation of empty layers, and visibility change notification," ultimately still resolving toctx.effect.
The runtime accumulates these undo functions in order in the fiber's disposables list. When the plugin is unloaded (fiber.dispose()), _unload() clears this list and executes the undo functions within it—within the same effect, in reverse order of registration, like stack unwinding (disposables.splice(0).reverse()). And so: listeners are unregistered, tools disappear from the registry, timers are cleared. The entire runtime structural space returns to its pre-installation state—no restart, no residue.
The execution of undo functions does not only occur at unload time—the author may manually call the disposer returned by a registration API at any time to unregister early (for example, llm-retry in its own undo logic first manually unregisters the listener, then aborts and drains in-flight retries); hot reload (fiber.restart()) and withdrawal of injected services (dependency loss triggering _refresh → unload and reload) likewise first execute undo functions before reloading. Reversibility is not a "deathbed reckoning" but a capability to subtract at any time.
There is also an easily confused question: on whose ledger are undo functions recorded? Plugin code can be instantiated multiple times within a single process; each ctx.plugin() mount produces an independent fiber—with its own derived ctx chain, its own disposables, and its own lifecycle. Every agent scope in DSH is created this way: createScope internally mounts a dedicated fiber via ctx.plugin(scope) (dsh/packages/core/scope/src/index.ts); everything registered within the scope (tools, guards, listeners) is recorded on this fiber, and scope.dispose() independently cleans it up—the isolation of "one plugin code, multiple instances" relies precisely on the fiber's independent lifecycle. ctx.isolate(name), by contrast, only changes the storage slot for services (same-named services can coexist without conflict) without producing a new fiber—registrations made on the isolate sub-ctx still follow the original fiber. Thus the ctx chain (extend prototype chain) simultaneously determines two things: visibility (which service implementation is read along the chain) and ownership (ctx.fiber determines which ledger registrations are recorded to and to which subtree cascading unloads apply); service configuration overrides (intercept) also inherit along this chain.
Expressed as a formula, installing this plugin corresponds to:
App = Base + (Plugin, reverse of Plugin's changes to the runtime structural space)
= Base + Δ(tool + listener + timer) + Δ⁻¹(set of undo functions)
Unloading is applying Δ⁻¹, equivalent to performing a subtraction on the runtime structural space: App = Base + Δ - Δ = Base. Note that what is undone is always structure, not business data: the logs read when stat_summary executes, the counts accumulated by countFailure—none of these are "reversed." DSH's reversibility targets the runtime structural space (who registered what, who is listening to what, who occupies what resources), not business state. This is the precise meaning of "Delta can be positive or negative": the objects of positive and negative operations are structures, not states.
One detail: DSH's "reverse order" guarantee is strict LIFO only within a single effect; the multiple top-level effects of a fiber are cleaned up concurrently during unload—their contributions are mutually independent and require no ordering. If a teardown sequence has ordering requirements, the official guidance is to write them into the same effect and await them in sequence within the same undo function.
With this example, the paper's conclusion that "installing plugins 1, 2, 3 and then unloading plugin 2 is observationally equivalent to installing plugins 1 and 3 from scratch" becomes intuitive: all of plugin 2's changes are recorded on its own fiber and fully reversed upon unload; plugins 1 and 3's contributions are mutually independent—dependencies are explicitly declared via inject, and when plugin 2 unloads, plugins depending on it are notified to re-resolve, leaving no dangling references. Therefore, the runtime structural space after unload is equivalent to what would result from "installing only plugins 1 and 3"—just as executing a set of mutually independent statements A; B; C and then withdrawing B leaves a residual state identical to directly executing A; C. The difference is that DSH turns "the effect of withdrawing B" from a verbal promise into a runtime-guaranteed, verifiable mechanism.
Going one step further: since installation and uninstallation are both additions and subtractions on the runtime structural space, "dynamic plugin updating" becomes a natural corollary—apply Δ⁻¹ to the old plugin, then apply Δ to the new plugin:
App = Base + Δ(old) - Δ(old) + Δ(new) = Base + Δ(new)
This is the entire mechanistic secret of DSH's hot update (HMR) and runtime self-modification: no restart is needed because changes to the structural space are inherently reversible. A production-grade instance of this pattern in DSH is the llm-retry plugin (dsh/packages/llm/llm-retry/src/index.ts): it listens for agent/request-error events to implement retry policies and registers "abort all in-flight retries and await their drain" as its undo function via ctx.effect—the same skeleton as the example above.
Finally, it is worth making explicit that in DSH, this "subtraction" works not because the runtime autonomously infers how to undo, but because each plugin writes and hands over its own undo function at installation time—"Δ is born with its inverse"; the inverse is a contract written by the plugin author in apply, not a property derived by the runtime. The paper is clear on this point: the runtime does not verify whether the undo function is correct (that is the component author's obligation); what it guarantees is only that—your undo function will be saved, will be executed, and will not be executed twice.
3. The Difference Between Pi Agent and DSH
Pi Agent is also highly plugin-based, so if "everything is a plugin" merely means "the framework supports many extension points," then that is indeed not DSH's invention. The real difference between the two lies not in the number of plugins, but in one question:
Are extension coordinates predefined by the core, or can they be continuously generated by plugins through service/inject relationships?
"Coordinates" is the key term for understanding DSH. Let us first unpack its meaning.
3.1 ctx Is the Interface to the Runtime Structural Space
DSH's runtime structural space consists of three parts:
-
A shared coordinate table. The entire runtime has exactly one service table (the root ctx's ReflectService; all child ctxs share it along the prototype chain). Each slot in the table is located by the coordinate pair "service name + isolation tag"; the slot records the implementation value and its owning fiber (
dsh/vendor/cordis/src/reflect.ts). -
The fiber tree. Each
ctx.plugin()mount produces a fiber (plugin instance) and its dedicated derived ctx (parent.extend({ fiber: this }),dsh/vendor/cordis/src/fiber.ts). The fiber is the lifecycle unit of the structural space: when dependencies are satisfied, when activation occurs, and which resources are reclaimed on unload are all determined by it. -
The ctx chain. The ctx that plugins receive is actually a Proxy—property reads and writes are intercepted and translated into coordinate lookups (
dsh/vendor/cordis/src/context.ts). Each ctx carries a "tag view" (service name → isolation tag); when queryingctx.database, the current ctx's view first translates the service name into a slot in the table; names not modified by child ctxs inherit the parent ctx's view along the prototype chain. An intuitive example: the parent Agent'sctx.shellpoints to the local environment, while the child Agent'sctx.shellpoints to a restricted sandbox—tool consumers still call the same key, but the resolved implementation differs.
Thus, the entire runtime structural space can be viewed as multiple delta layers stacked; ctx is the interface (Proxy view) exposed to the outside; the fiber is the unit responsible for dynamic lifecycle management within the space; "service name + isolation tag + view chain" is the coordinate; registered items are values at coordinates. All of a plugin's actions—registering tools, subscribing to events, providing services—are writes to some coordinate, with the undo function for "erasing the value at this coordinate" handed to the runtime on the spot. The tool registry is precisely such a coordinate table: each tool occupies a slot keyed by name—same-name registration within the same layer directly errors (duplicate error, NamedEntries.insert in dsh/packages/core/scope/src/store.ts); same-name across layers results in the nearer layer shadowing the farther one (dsh/packages/core/tools/src/index.ts). Events are keyed by event name; services by service name... What "everything is a plugin" unifies is precisely a key-value coordinate space.
3.2 ctx Chain vs. Delta Layers
Compare with Nop's delta file system: the same resource path exists separately in multiple layer directories; lookup proceeds from high to low layers, taking the first hit—files in higher layers entirely shadow files in lower layers. DSH's ctx chain: each ctx contributes a "service name → isolation tag" mapping; names not modified by child ctxs inherit the parent ctx's mapping along the prototype chain, all ultimately pointing to the same shared service table—reading ctx.database takes the nearest tag mapping along the chain. Both follow the same "layered namespace + nearest-first" pattern, differing only in physical layout: Nop's layers carry content (files live in layers; lookup spans layers), while DSH's layers carry routing (values live in the shared table; layers only determine which slot to look up). x:extends is similar—its semantic skeleton of "most specific layer overrides; the rest is retained as fallback" is the same thing as prototype-chain lookup.
However, the following differences exist between them:
-
Merge granularity.
x:extendsperforms node-level merging: base and current file are matched by child-node identity; attributes are overridden at the property level; unmatched content in the base is retained in the result; specific nodes can also be deleted viax:override="remove". DSH's provide is whole-value shadowing—a child ctx's same-named service entirely covers the parent implementation; the parent implementation remains alive in the table but is completely invisible in the child's view (there is no operation to "retain part of the parent implementation"). DSH's "retention and merging" only appears when the value is data: intercept configurations merge in order along the chain (descendants override ancestors, similar tox:extends's property-level override); the tool registry does union combination across layers (entries from all layers are visible, same-name takes nearest shadowing, similar to delta directory merging). But when the value is an object (service implementation, listener), merging does not exist—two closures cannot be merged; they can only be replaced, or strung into an onion via event waterfall. - Temporality. Nop's delta layers are determined at assembly time and, apart from the tenant isolation layer provided by the nop-dyn module, generally do not change dynamically. Each DSH layer is bound to a fiber and can be created, restarted, and unloaded at runtime; unloading triggers reconciliation in dependents. Thus the ctx chain is not only spatial layering but also temporal generation: spatially it determines coordinate visibility; temporally it determines lifecycle ownership. The paper's "spatiotemporal composability," when traced to source code, is precisely these two orthogonal dimensions: in which ctx/realm a plugin resides and which layer of services it can see; when dependencies are satisfied, when the fiber activates and exits, and which resources are reclaimed upon exit.
-
The carrier of inverses. DSH returns a disposer (closure function) on the spot at
ctx.effectregistration time; undo = calling a function once. Nop's inverse is a structural delta (x-diff extracted after the fact or pre-written by the author); undo = writing/using a new delta. The difference between the two is: opaque modification function vs. declarative structural delta.
As a side note, DSH does have a part that genuinely corresponds to Nop's compile-time structural space—its configuration tree (Profile → Bundle → patch → command line): configuration overrides are "whole-segment replacement without deep merging," considerably weaker than x:extends's node-level merging. Moreover, Nop's dump can show item by item which delta layer each selection comes from, whereas DSH's configuration tree displays only the final superimposed result—this further shows that the Nop platform provides a more refined delta merging mechanism.
3.3 Extension Coordinates Can Be Generated by Plugins
Pi's plugin-based design on the surface can also be written as:
Agent = AgentLoop + Extension1 + Extension2 + ...
But if we analyze it using the structural coordinate system perspective from reversible computation theory, we can clearly see that Pi Agent essentially first stipulates that "the system is an Agent," with the core Agent Loop defining the main control flow, and then opens extension points along the Loop's lifecycle and data path.
Fixed Agent Loop
├─ model/provider
├─ tools
├─ prompt/context
├─ events/hooks
└─ UI / session extensions
In other words, Pi's extension space is Ext(AgentLoop): tools, hooks, providers, and context are all slots pre-defined by the Agent Loop; plugins can only fill these slots. DSH, in contrast, has an extension space of Compose(Plugin₁, …, Pluginₙ): plugins can provide arbitrary services, and other plugins can inject them—thus new extension points are not "designed" by the core but "emerge" from the plugin ecosystem:
Δ₁ = provide(ServiceA) # Plugin 1: defines a new coordinate
Δ₂ = inject(ServiceA) + provide(ServiceB) # Plugin 2: consumes it, then defines the next coordinate
Δ₃ = inject(ServiceB) # Plugin 3: continues consuming
The composite result is an open dependency graph ServiceA → Plugin2 → ServiceB → Plugin3, not a radial structure centered on the Agent Loop. Deltas can not only fill existing coordinates but also introduce new coordinates—this is the precise meaning of "DSH is more free than Pi Agent."
Of course, freedom does not mean no bottom line; the following constraints actually exist:
- The Cordis kernel remains the meta-layer: Context, service identifiers, provide/inject, lifecycle, scope, and reconciliation—these composition rules are defined by it. What it constrains is "how structures are formed," not "which business structures are allowed to be formed."
- Services like tools, session, and llm are still officially predefined—but they are "de facto standards established through the plugin mechanism," in principle replaceable, not kernel-hardcoded slots.
3.4 Two Paths of Change in the Structural Space
In DSH, when a service's Provider changes, the runtime handles it along two paths depending on impact scope:
-
Light path (registry replacement): Providers of stateless capabilities (e.g., Web Search) are registered in a Map; the implementation is selected at call time based on current configuration (
dsh/packages/web/web/src/index.ts)—undo the old registration; consumers need not restart. - Heavy path (dependency convergence): When a long-held Service disappears, its consumers exit, old fibers clean up, and when a new service appears, consumers re-activate according to the new relationships—this is reconciliation.
The light path embodies coordinate positioning in the structural space, while the heavy path fully demonstrates the paper's emphasis on "spatiotemporal composability": when a Provider disappears, its Consumers proactively exit; after old fibers are cleaned up and a new Provider appears, consumers re-activate according to the new relationships. Space (who sees which service in which ctx) and time (when dependencies are satisfied, when fibers activate and exit) are linked by the same mechanism.
Pi's lifecycle management is far cruder by comparison—one could say it only models its own AgentLoop lifecycle and extension points. On /reload, Pi sends session_shutdown to the old Extension runtime, reloads settings, Extensions, Skills, and prompts, constructs a new runtime, then sends session_start(reason: "reload"). Call stacks of old commands do not automatically disappear because of reload—continuing to use the old ctx after await ctx.reload() can crash into an invalidated runtime. The official documentation therefore requires treating reload as the endpoint of the current handler, with long-lived resources created in session_start and idempotently cleaned up in session_shutdown.
Pi does not model relationships between Extensions; their coordination is maintained entirely through convention, testing, and code review. There is even less unified governance of Extension-internal lifecycles. session_start and session_shutdown are two separate event callbacks; side effects and cleanup logic are written in two places; complete cleanup cannot be structurally guaranteed.
In summary, the difference between Pi and DSH can be stated as:
- Pi: an open framework centered on the Agent Loop;
- DSH: a runtime structural space grounded in plugin composition.
4. Further Analysis from the Perspective of Reversible Computation
(Generalized) Reversible Computation theory (GRC) is a foundational theory about software construction and evolution that I proposed around 2007. Its core idea can be expressed as a formula: App = F(X) ⊕ Δ—X is a structural definition from another domain, F is an operator that transforms/generates X into the current domain, Δ is a structured delta, and ⊕ is some operation satisfying delta associativity. As a concrete technical implementation, it can be expressed as:
App = Delta x-extends Generator<DSL>
The Nop platform is a reference implementation of reversible computation theory that I released in 2023. It encapsulates compile-time structural space reversible computation behind a Loader interface through a DeltaLoader:
Loader :: Path -> Model
DeltaLoader :: Possible Path -> Possible Model
Possible Path = DeltaPath + StdPath
DeltaLoader<DeltaPath + StdPath> = DeltaLoader<DeltaPath> x-extends DeltaLoader<StdPath>
DeltaLoader<PossiblePath> = DeltaModel x-extends BaseModel
In other words, simply replace the loaders for external description files in your application—JsonLoader, WorkflowModelLoader, etc.—with Nop's DeltaLoader, and you automatically obtain reversible computation support.
Reversible computation theory is a universal foundational theory about Delta and delta operations; all practices involving Delta can be explained and analyzed within this theoretical framework. In my previous articles, I have already provided corresponding analyses of Docker, React, Kustomize, Git, and many other technologies.
For example, the construction structure of Docker images can be viewed as:
DockerImage = DockerBuild<DockerFile> overlay-fs BaseImage
And Kustomize can be viewed as an application of Docker's layered filesystem idea to the application configuration layer:
K8sConfig = KustomizeBuild<KustomizationYaml> patch-merge BaseYaml
Based on the analysis in the previous sections, DSH's practice can likewise be subsumed under the reversible computation image of delta merging in a structural space, except that its definition space is the runtime structural space with lifecycle management. Below, we will conduct further theoretical analysis in conjunction with the DSH paper.
4.1 Reversibility Must Be Defined in the Structural Space
The word "reversible" exists in multiple different contexts within computer science, each with different meanings:
| Context | Object of Inversion | Representative |
|---|---|---|
| Reversible computing | Computation process (reverse execution of logic gates) | Landauer reversible computation |
| Transactions/compensation | Business side effects | Database ROLLBACK, Saga |
| Cordis/DSH | Changes to framework-managed runtime structures | Undo functions of effects |
Cordis's reversibility is the third kind: service provisioning, dependency binding, event subscription, interceptor chains, and lifecycle relationships are all structural changes that the framework has "registered and can see"; undoing them is neither replaying execution history in reverse nor rolling back business data. The object of reversibility is always structure, not state.
The DSH paper explicitly introduces a formal definition of the structural space—essentially a table with coordinates, consisting of three parts:
-
Coeffect context Σ (Definition 22): a partial function table from keys to values
σ: K ⇀ V_k—keykis the coordinate;V_kis the content bound at that coordinate; the entire table is the runtime structural space itself. Reading and writing coordinates has only two primitives (Definition 23):get(k)(preconditionk∈dom(σ)) reads a coordinate;set(k,v)(preconditionk∉dom(σ)) writes a coordinate—the latter is precisely the formalization of "single-writer": a coordinate can only be written once, and set itself is an effect function with an inverse (set(k,v) ∈ 𝔈Σ*); the inverse (undo) is unified with coordinate read/write. -
Isolation realm table ρ: K⇀R (Definition 28): the coordinate translation layer—resolution goes through two levels
k → ρ(k) → σ(ρ(k)); the same key resolves to different bindings through different realms. This is the mechanism for "coordinate namespaces/multi-tenancy." -
Registry and committed view (Definition 45/46): the registry is a table keyed by fiber name (coordinates of lifecycle units); the committed view
ω: d → Nresolves each key declared by a component to the fiber providing it (coordinate resolution).
A component is defined as a triple (d, p, e) (Definition 43): d declares which coordinates it reads, p declares which coordinates it writes, and e is an effect function with an inverse. Definition 58 clause 2 requires that the p sets of different fibers be pairwise disjoint—"single-writer" is thus implemented at the component level.
Here we must distinguish the two layers of "single-writer" meaning, to avoid misreading it as "only one contributor per coordinate": what single-writer locks down is the binding ownership of keys—each coordinate is provided once by one provider fiber; but when the value at that coordinate happens to be a table (event listeners, tool tables), multiple fibers can still each make their own additions and deletions on this table, with each operation carrying its own inverse and unloading only its own entry (the paper's §3.3.2 states verbatim: "either registration can be withdrawn while the other stands"). What is truly locked down by "single-writer" is the act of "deleting someone else's contribution"—this is precisely the "no deletion" discussed in 4.4.
Compared with reversible computation theory, DSH's design is clearly a special case of the structural space; in many more application scenarios, the single-writer requirement can be relaxed, and the coordinate system can certainly be extended to tree-structured coordinates rather than only the current flat mapping coordinates.
Is a structural coordinate system necessarily required for reversibility? I have already argued this in my earlier theoretical article A Programmer's Analysis of Reversible Computation Theory: First, the independent existence of deltas implicitly requires that the original system possess an explicit coordinate system—for a delta to be independently stored, independently managed, and ultimately merged with the base, one must know precisely where each local change should apply; one must be able to transparently traverse all structural barriers and directly apply changes at the coordinates where perturbations actually occur. The first requirement of any design with delta characteristics is that the system internally possesses a systematic, uniquely identifying positioning mechanism. Second, the coordinate system must be stable—Docker's file paths are stable coordinates (adding/removing one file does not affect the coordinates of other files); Git's line numbers are unstable coordinates (adding/removing one line causes all subsequent line numbers to drift). Unstable coordinates cannot support the independent existence and free composition of deltas; it is precisely because the coordinate system is stable and precise that mathematically precise delta operations (add/delete/modify, merge, invert) can be defined on it—on chaotic textual differences this is impossible.
In my previous articles, I abstracted coordinates to a minimal definition: "any unique identifier supporting get(path)/set(path,value) is a coordinate." The DSH paper's formalization corresponds verbatim to this minimal definition: Definition 22's coeffect context is precisely a key→value table; Definition 23's get(k)/set(k,v) are exactly these two operations; and keys are stable named coordinates (adding a new key does not affect other keys' coordinates), not Git-style positional coordinates—the DSH paper arrives at the same conclusion from another theoretical path: coordinates first; only then do deltas have meaning.
The concept of coordinate systems in the runtime space is not new. Dijkstra, in his classic paper Go To Statement Considered Harmful, pointed out that his scientific reason for opposing goto was not "ugly code," but that unconstrained goto makes "it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress"—whereas code written with structured programming naturally constitutes an objective coordinate system (text index + loop dynamic index + nesting depth) independent of the programmer, by which one can intuitively map static programs (text space) to dynamic execution processes (time space) and locate at which point runtime behavior currently stands (see my earlier article NopTaskFlow: A Next-Generation Logic Orchestration Engine Written from Scratch). The difference here is that the coordinate system provided by structured programming can only assist understanding of runtime behavior; it cannot support further computation—you cannot apply deltas, inversions, and merges to execution traces. Reversible computation's deepening of Dijkstra's insight is precisely this: not only must there be a coordinate system, but the coordinate system must support delta operations and be genuinely used for software construction.
4.2 The Delta Algebra on a Single Coordinate: Commutativity and Inverses
After defining the coordinate system, we need to define the operations at each coordinate point. Here we must first introduce the independence of coordinate points.
Operations at different coordinates are naturally independent. In the DSH paper, the proof of Theorem 40 is extremely compact—its essence is a single sentence: operations on different keys are "each reading and writing one key alone and the two keys differing": each reads and writes only its own coordinate's value; if the coordinates differ, the operations are pairwise independent—transformations commute and do not perturb each other's inverses. This is the formalization of "different coordinates do not interfere with each other," and it is the foundation of all algebraic conclusions on the entire structural table.
Next, we must consider whether operations on a single coordinate satisfy commutativity. The paper defines each coordinate's own operations and equivalence relation separately (Definition 24), then uses this to determine commutativity (Definition 39): a key is commutative if and only if any two operations on it are pairwise independent. "Independent" is read in terms of observational equivalence—if the results of two orderings differ only in values indistinguishable under that key's ≃_k, they still count as commutative (the paper's allocator example relies precisely on the tightness/looseness of ≃_k to draw this line). Thus the criterion is singular: can that coordinate's operations distinguish the two orderings—not an a priori assumption of order independence. Substituting two typical classes of coordinates:
- Keys whose values are tables are commutative—registration-type operations are the representative case: the paper cites route and event-listener registrations; regardless of order, what remains is the same table, and either can be independently withdrawn (tool registration is structurally isomorphic; see 3.1).
- Keys whose values are ordered chains are non-commutative—"middleware inserted before another sees a different request": middleware inserted before someone else sees a different request than middleware inserted after; the ordering is genuinely distinguishable.
Applied to DSH's event system, "whether order matters" depends on the dispatch mode:
-
ctx.emit: synchronously notifies all listeners and ignores return values—listeners are mutually independent; ordering is genuinely irrelevant; registration and deregistration both commute. -
ctx.waterfall: strings listeners into an onion middleware chain where each listener must callnext()to release downstream—this is exactly the "ordered chain" the paper lists as a non-commutative counterexample; a listener that forgetsnext()swallows downstream behavior. Order genuinely exists here and genuinely affects results; DSH does not treat it as commutative but delegates governance to the mechanisms below.
When commutativity fails: ordering is carried by coeffects. The paper splits the system into two halves: the commutative part is carried by effects (can be undone in any order); the order-sensitive part is carried by coeffects (§3.3.2), with ordering imposed in three places:
- Within a single fiber: the accumulator undoes in LIFO (Theorem 16); reverse-order undo requires no additional assumptions.
- Across fibers: through declared coeffect ordering—activation requires dependencies satisfied (providers before consumers); providers may unload only after all consuming fibers have exited (Theorem 63's ordering and guard).
- At the meta-theory level: progress and confluence presuppose acyclicity of dependency relations (Definition 65's ≺, Theorems 66/73).
Different event types = different coordinates = their own delta algebras (Definition 24). Each key carries a triple (V_k, ≃_k, A_k): value type, observational equivalence, operation set. Different event types such as agent/request-error and pre-execute in the tools event vocabulary are different coordinates—in DSH, this "algebra of a coordinate" is given by the contract on event declarations: every event must declare a dispatch mode (@mode: emit/parallel/serial/bail/waterfall; if the last parameter is next, it is waterfall). The five modes are five composition semantics—emit/parallel are union broadcast (the paper's "table" criterion), serial/bail are ordered short-circuit, waterfall is an ordered onion chain (the "chain" criterion). Thus, a coordinate has exactly one combining operation. To ensure the contract is not violated, DSH uses tooling verification rather than discipline alone: the catalog generator forces every event to declare @mode and requires the signature to be consistent with the mode; the generated "event-producer-consumer" matrix checks each event's declared mode against its actual dispatch method; the doc-sync gate re-generates and diffs "code ↔ declarations." Thus "one event, one associative law" is guaranteed jointly by "declaration + verification," not chosen ad hoc at runtime. The paper's proofs are therefore modular: the algebra within each coordinate is verified separately (Definition 39 → Theorem 42: if all involved keys are commutative, effect functions are pairwise independent); independence between coordinates holds automatically (Theorem 40); then via Corollary 21, the system's overall reversibility is composed (Theorem 61 Recovery exactness, Theorem 73 Confluence). "First distinguish by coordinates, then separately establish commutativity/associativity" is precisely the standard approach reversible computation theory uses to complete modular proofs.
As for inverse operations at coordinate points, DSH's assumption is that the existence of inverses is unconditional—every effect function carries its own inverse (the definition of 𝔈Γ); inverses are given by construction and independent of commutativity (this is exactly why the LIFO undo in point 1 above "requires no additional assumptions"). Commutativity does not determine "whether we can invert" but **in what order inverses execute and whether they can freely interleave with other fibers' inverses*: if coordinates commute, inverses can execute in any order and interleave with others' inverses (Corollary 21); if coordinates do not commute, inverses can only be LIFO or delegated to coeffect ordering.
4.3 Unified Governance of Dynamic Resources
A fundamental problem facing the runtime structural space is the governance of dynamic resources: timers, connections, and the like are scattered throughout the process and cannot be enumerated by the framework—if they leak when plugins unload, "restoring the original state" becomes empty words. DSH's solution is not to track resources themselves, but to require that all managed dynamic resources be converted into handles at creation time and registered as delta entries in the coordinate system: ctx.effect(() => { const timer = setInterval(heartbeat, 60_000); return () => clearInterval(timer) })—the effect body creates the resource on the spot and returns the teardown action; the runtime records it, along with a label, in the current fiber's disposables ledger: one slot is one coordinate; the label is a human-readable name (e.g., 'tools.register()', default 'anonymous'); fiber.getEffects() allows enumeration at any time (dsh/vendor/cordis/src/fiber.ts). This is precisely designing the fiber's disposables as a handle management table: coordinates (slots) + names (labels) + enumerability (getEffects)—otherwise unenumerable dynamic resources in the process could not be governed at all.
Once converted, this entry has no ontological difference from listener or service entries—it is likewise a delta of "coordinate slot + value + removal semantics"; the underlying implementations of ctx.on and ctx.tools.register are themselves ctx.effect (registration actions return unregister functions on the spot; see the call chains in Section 2). The only difference lies at the moment of removal: for registration-type entries, the value still resides in a shared table (event hook arrays, tool layers); removing it from the table is the undo, and after removal one can inspect the table to verify the entry's disappearance. For resource-type entries, the coordinate system contains only this handle; removing it from the coordinate system triggers an associated teardown action (clearInterval, close connection); the framework guarantees the handle is saved and executed once, but does not verify whether the teardown is correct—that is the component author's contract (see the paper's honest declaration at the end of Section 2). Precisely for this reason, the paper says such effects are tracked like other effects but cannot be named by other components in specifications, and thus do not participate in dependency ordering. Fully implicit side effects (mutating an external variable without leaving any entry) never undergo conversion; the framework cannot observe them and cannot reverse them. This is why Cordis firmly limits reversibility to "managed operations": the prerequisite for reversibility is coordinatization—all managed dynamic resources must be converted into handle deltas on coordinates; once converted, they are of the same kind as any coordinate entry, with the only difference being "whether removal carries a teardown action."
4.4 Single-Writer + Accumulation + No Deletion—Inverses Without Independent Negative Elements
DSH's runtime superposition follows three conventions, which can be precisely stated as "single-writer + accumulation + no deletion." Single-writer—as argued in 4.1, it constrains the binding ownership of keys. In implementation, DSH enforces this at runtime: same-name registration in the same layer directly errors (duplicate error in NamedEntries.insert); across layers, it does not rewrite others' layers—each writes its own layer, and reads take nearest shadowing. Accumulation—registration operations only insert; the runtime structure grows monotonically with registration; composition semantics are twisted composition of (contribution, inverse) pairs. No deletion—the runtime provides no primitive to "delete someone else's contribution"; the only removal mechanism is executing one's own inverse: event entries work this way—each entry belongs only to its registrant; deregistration requires holding the listener reference—deletion is capability-based; you must hold the handle to remove it; you can only subtract what you yourself added. In other words, DSH has inverses paired with creation (dispose) on runtime coordinates, but no independent negative elements (no way to express "subtract someone else's contribution"). This seemingly restrictive design in fact reveals a principled conclusion: plugin-level delta customization fundamentally cannot bypass the delta algebra of the structural space. The reason is that delta merging requires the object being modified to possess a structural coordinate system—otherwise one cannot locate "at which point to apply the delta." DSH's plugin internals are imperative TypeScript code (ctx.on/ctx.provide executed in the apply callback); the "structure" of imperative code is control flow and closures, with no stable addressable coordinates. This is the fundamental reason DSH's patch can only operate at the entry-id level: without coordinates inside the plugin, there are no deltas inside the plugin.
To enable delta customization within plugins, there are only two paths: first, make plugin internals declarative—express the plugin's contributions as DSL structures, establish field-level coordinates, then use the x-extends machinery to perform arbitrary-depth add/delete/modify (Nop's route); second, define a delta algebra on the runtime structural space—but this conflicts with DSH's "single-writer + accumulation" design; to have runtime negative elements, one must answer the questions the paper avoids: who may delete whose contribution? Does deletion satisfy associativity? The conclusion is therefore: whether a plugin is structure (customizable) or code (replaceable only) determines the upper limit of customization capability—Nop's Delta packages are themselves structures, so they can be customized down to the field level; DSH's plugins are code, so customization can only stop at "the entry point of the code."
4.5 From Generator to Applier: The Dual Identity of apply
The core formula of reversible computation is App = F(X) ⊕ Δ: F is a Generator that constructs structures from input X in some domain, with delta Δ superimposed. Comparing with DSH's implementation, we find that the plugin's apply(ctx) function serves dual roles as both Generator and Delta Applier: based on inputs such as configuration descriptions, it dynamically produces modifications to the runtime structural space and applies these modifications directly within the same execution step.
Each ctx.on / ctx.provide / ctx.effect produces a reversible structural change entry at runtime—registering a listener, providing a service, starting a timer—while simultaneously handing the operation's inverse function (undo function) to the runtime. Thus the semantics of the apply function can be understood as "defining what one contributes to the current Context," with that definition being immediately realized as concrete structural changes during execution.
The current design of apply essentially merges "delta generation" and "delta application" into a single step—plugin code performs registration in place rather than first returning a declarative Delta description for the framework to apply uniformly. One can also imagine a functionally equivalent but mechanistically different design: ΔP = setup(config); C' = apply(C, ΔP)—the plugin first returns a declarative Delta, and the framework then applies it to the context. This imagined design is exactly the route the Nop platform takes—it separates delta generation (x:gen-extends) from delta application (x-extends merging) into two steps, with an external engine fully taking over delta operations. Under that design, the structural correctness of undo is guaranteed by the merge algebra—as long as the delta definition itself is legal, reverse extraction (x-diff) necessarily returns to the original state. Cordis, because of the opacity of inverse functions, can only trust what plugin authors provide.
function setup(config): Delta {
return {
provide: [{ service: ServiceA, value: impl }],
inject: [{ service: ServiceB, consumer }],
}
}
apply merges generation and application into one step, and this single execution step requires "time to stand still" within the application window. If registration actions apply the delta in place while unfolding it, then to guarantee "after application, it can be restored to the unapplied state," one must ensure that no other changes occur during that registration's execution—otherwise the "application-time state" captured by the inverse function may have been contaminated by concurrent modifications. This is why DSH's asynchronous effect unfolding checks at every step whether runner.epoch has changed (aborting the unfolding immediately upon any dependency change), and why the inertia mechanism guarantees atomic completion of a single transition—together, these are the engineering realization of "frozen time": within a single registration's unfolding window, changes to related coordinates are frozen. The paper provides a mathematical counterpart to this engineering choice: Theorem 61 (Recovery exactness) and Lemma 71 (Transposition) jointly rely on the fact that the iterators and accumulators carried by a step are fixed by that step and the state at that time, and are defined at every state, so they can be evaluated at states that have left the current position (paper §4.4.2). DSH's epoch check chooses to stop the same class of problem before it happens—aborting unfolding at the first sign of dependency change, preferring to discard the contaminated half-completed result outright rather than relying on the theorem's tolerance under interference. DSH chooses one-step execution + local frozen time (epoch check + inertia); Nop chooses compile-time merging (naturally frozen because there is no runtime concurrency at all)—two choices arriving at the same destination, both ensuring "no interference within the application window."
4.6 Which Deltas Must Be Defined in the Runtime Structural Space
Comparing with existing implementations in the Nop platform, we find that many registrations could be moved up to the compile-time structural space for description. For example, in xbiz.xdef:
<!-- Base model: declarative registration of business actions; name is the coordinate (xdef:key-attr="name"); when expresses runtime conditions -->
<biz x:schema="/nop/schema/biz/xbiz.xdef" xmlns:x="/nop/schema/xdsl.xdef" xmlns:c="c">
<actions>
<action name="computeQualityScore" when="tenant == 'A'">
<arg name="entityData" type="io.nop.biz.crud.EntityData"/>
<arg name="svcCtx" kind="ServiceContext"/>
<source>
<c:script><![CDATA[
// Action implementation
]]></c:script>
</source>
</action>
</actions>
</biz>
<!-- Delta customization: without modifying the base model, override the implementation at the same coordinate in the delta layer, or remove it entirely -->
<biz x:extends="super" x:schema="/nop/schema/biz/xbiz.xdef" xmlns:x="/nop/schema/xdsl.xdef">
<actions>
<action name="computeQualityScore" x:override="remove"/>
</actions>
</biz>
If dynamic judgment of whether to register is needed, a when condition can be added to express runtime evaluation. In fact, business actions, workflow steps/listeners/actions, and task-flow steps in the Nop platform are all declarative nodes in the compile-time structural space, keyed by name/id. The information of "what to register" is entirely expressed in the structural space as DSL; delta merging (x-extends) is completed at compile time/load time; at runtime, only the expanded model is loaded and interpreted. Expansion actions (x:gen-extends generation + delta merging) converge in the model loader (ResourceComponentManager).
Once "what to register" becomes a declarative structural definition, delta customization (composition, undo, reverse extraction) all become operations in the structural space; at runtime, only the expanded result needs to be applied. If DSH adopted a design similar to Nop's, it could achieve customization of plugins themselves—that is, without modifying the core or the plugin code at all, using Delta to customize the fine-grained internal features of a plugin. Currently, DSH's approach can only be controlled through configuration or wholesale replacement of plugins; it cannot achieve further delta-ization of the plugin itself. In other words, Plugin is a delta, but the delta of a delta lacks an appropriate technical carrier. In reversible computation theory, the delta of a delta is also an ordinary delta and can be implemented using the same mechanism.
The key here is that reversible computation theory itself permits positive and negative elements to aggregate into a new legal element—for example, Delta = (+field A, -field B). This is the theoretical foundation for reversible computation's support of fine-grained customization. But DSH's runtime structural space does not support negative elements, so reversible computation can be seen as providing a larger solution space, with DSH providing one specific solution within that space.
Nop's "compile-time expansion" assumes that structures remain essentially unchanged after loading (runtime changes are realized through model reloading); but if structures require high-frequency dynamic changes (self-modifying harness scenarios), the runtime structural space's reversibility mechanism (inverse tracking + reactive triggering) is a necessary complement. The ideal form is a combination of both: the structural space handles the delta algebra of "what to register" (proven by Nop); the runtime structural space handles the reversible management of "how structures activate/unload" (formalized by DSH).
On either side of this boundary lie two different composition topologies. DSH's plugins do not compose directly with each other: Plugin1 and Plugin2 do not treat each other as operands; they both compose vertically onto the runtime coordinate system—a star topology where the kernel is the de facto identity element. Thus "composition without Base" is trivial in DSH: zero is the empty coordinate system. Nop's delta chains, by contrast, form a chain topology: Δ₃ ⊕ Δ₂ ⊕ Δ₁ ⊕ Base; any segment of a delta chain is itself a legal delta (guaranteed by associativity) and can be composed independently of any specific Base and attached on demand.
So, can all registrations be moved up to the structural space? No. There exists a class of registrations whose content depends on information only known at runtime—the most typical example is DSH's agent-level scope: when the same plugin code executes in different agents' scopes, the specific content registered may differ completely (tools.presentAs takes effect only for the current scope; tools.guard() adds guards only for the current agent)—what is registered depends on who this agent is, and this information does not exist at compile time. The resolution order of the scope chain is agent → preset → global (nearer shadows farther). So the dividing line is not determined by a preference for "whether it can be moved up," but by the epistemological boundary of "when the registration content becomes knowable": whatever can be determined in the structural space is moved to the structural space; whatever can only be determined at runtime stays at runtime and is managed with reversibility mechanisms.
4.7 Reversibility (Capability) and Reactivity (Governance) Are Orthogonal
One important concept still needs clarification: dynamically checking dependencies and proactively triggering activation/deactivation—this mechanism is itself unrelated to reversibility. The DSH paper's emphasis on spatiotemporal composability may mislead readers into thinking that reactivity is an inseparable part of the entire design system. But from the perspective of reversible computation theory, this is an independent concern. In Nop's implementation, DeltaLoader is a form of passive dependency tracking: after loading a model, it automatically records the modification times of all dependent resource files; when a dependency file changes, the model cache is invalidated. In Nop's implementation, cache invalidation is checked each time a model is fetched. It is easy to imagine transforming this into an active update mechanism: whenever a resource file updates, automatically refresh the corresponding cache, or even push the modification to relevant consumers.
This can also be seen clearly from DSH's implementation: _refresh (reactive dependency computation) and _unload (revertible unloading) are mutually independent methods—the reactive mechanism only handles "when to trigger"; the revertible mechanism handles "how to execute." Even with no reactive mechanism at all, reversibility still holds: manually calling fiber.dispose() will likewise execute all inverses in reverse order. Reactivity merely adds an automated policy to "triggering."
So what is the value of automatic triggering? It guarantees runtime validity, not reversibility. Reversibility guarantees "it can be withdrawn"; reactivity guarantees "it will not run into invalid runtime states"—when a dependency becomes invalid, a fiber depending on it, if it continues running, would read nonexistent services and enter structurally invalid states. Automatic triggering proactively unloads dependents at the moment a dependency fails, ensuring that any running fiber is in a legal state with all dependencies satisfied. Theorem 63 in the paper turns this legality into a provable ordering guarantee: providers unload strictly after their dependents (ordering); any consumer exits before the provider it depends on. Theorem 66 further guarantees that the guards of cascading unloads will eventually release (guard always releases)—the termination of dependency reconciliation is guaranteed by theorems, not by empirical hope. This also explains why Nop does not need reactivity while DSH does: Nop's structural space is static—composite results are either legal or fail at compile time; there is no intermediate state of "sudden invalidation during execution." DSH's runtime structural space is dynamic; dependency disappearance is a real and frequent event that must be actively governed. Reversibility is capability (can it be withdrawn); reactivity is governance (when to withdraw); capability guarantees withdrawal is possible; governance guarantees it does not run when it should not.
4.8 Observational Equivalence
Does executing an inverse operation guarantee restoration to the original state? In actual implementations, this can only be an idealization: physical state cannot be fully restored—free returns a memory block to the allocator but does not restore the heap layout from before malloc. The DSH paper therefore explicitly requires all state equations to be understood as "observational equivalence": two states are equivalent if and only if no observer can distinguish them (Definition 33). Observational equivalence is not an approximation imported from outside but assembled from the comparison criteria built into the coordinates themselves: each key carries its own ≃k (the second component of the Definition 24 triple, determining "whether two values at that coordinate count as the same"); context-level ≃ is the assembly of the individual ≃_k (Definition 33). The boundary of "observation" is delimited by the operation set 𝒜_k—≃_k cannot separate more than the operations on that coordinate can distinguish (in the paper's words: "calling the relation observational is a claim about each ≃_k, namely that it separates no more than the operations of k can tell apart"). Definition 34's indistinguishability ≈𝒜 is precisely the formalization using the operation set as test words.
Observational equivalence is the operational definition of "reversibility" in real systems: what we require is structural recoverability, not physical-state recoverability—this corresponds exactly to reversible computation theory's position that "deltas act on the structural coordinate system" (as discussed in 4.1: the object of reversibility is structure, not state).
Equivalence is a dedicated topic in reversible computation theory. First, representation-transformation equivalence: the same information can have different representations; equivalence between representations is at the information level, not the literal level (e.g., equivalent transformations among XML/JSON/AST)—observational equivalence's ≃_k is precisely the formalization of "comparing by information content, not representation details." Second, round-trip transformation equivalence: adjoint functors in category theory can be viewed as a generalization of reversible operations—a pair of functors L and R are not directly mutual inverses but "correct" the round-trip result through natural isomorphism (an import engine parses Excel into object data, and a report engine then regenerates Excel—style information may change in the process). GRC's practical system lists "lenses/adjoint functors" as concrete mathematical models ensuring the feasibility of "semantic round-trips." Observational equivalence is precisely this "correction" realized in software construction: round-trips need not be strict equalities; they only need to land back within the observational equivalence class—Nop's x:gen-extends generation and x-diff reverse extraction round-trip works this way; DSH's "install-uninstall" works this way too (Corollary 21 says unloading returns to the initial state; this "return" holds in the sense of ≃). In the language of adjoint functors, reversible computation requires not mutual inverses but "adjoints": a quasi-inverse corrected through the "natural isomorphism" of observational equivalence.
5. The Philosophy of Reversibility: Effectively Controlling Entropy Increase
Neither DSH's code implementation nor its paper is particularly complex—they are not hard to understand. But the concept of reversibility is unfamiliar to most people, and the necessity of the dynamic-update application does not seem high either. So is it over-engineering?
If we broaden our perspective beyond the well-known traditional notions within computer science, we immediately find that reversibility, at both the scientific and philosophical levels, concerns the most fundamental operating laws of our world. It should be said that the computer science field's current understanding of reversibility is still in a very preliminary stage; the exploration of its applications is far from sufficient. The Nop platform leverages reversible computation theory to solve the problem of coarse-grained reuse in large software systems, while DSH leverages reversible plugins to attempt to solve the problem of agent self-driven evolution—both are only beginnings.
The physical world we inhabit is governed by the Second Law of Thermodynamics (the law of entropy increase), which states that when a system evolves spontaneously, entropy never decreases—if a process is reversible, entropy remains unchanged; if irreversible, entropy increases. Software evolution follows the same law: large software systems universally experience a journey from order to chaos; adding features becomes increasingly difficult, fixing bugs introduces new bugs, and eventually the system is torn down and rebuilt. Reversible computation thus arrives at its core proposition: constant entropy implies reversibility—if every evolution of the system is reversible, then taken together, the system always remains in a determinate, rollback-able state; evolution does not accumulate chaos. DSH's runtime structural space is precisely pursuing this deterministic evolution: whatever is installed can be completely uninstalled; install N plugins and remove any one of them, and the system is observationally indistinguishable from "installing only the remaining plugins"—every step leaves no residue; entropy does not increase.
But in the real world, entropy increase is unavoidable: business data must be written, external side effects must occur, and code always contains some implicit coupling; forcibly demanding "everything reversible" merely causes irreversibility to disguise itself. Reversible computation's attitude is: we cannot control entropy increase, but we can choose where it occurs—concentrate irreversibility at explicit boundaries, letting it occur in the delta layer rather than in the core architecture. Nop concentrates incidental, random customer requirements into discardable delta layers, so that delivering to a new customer always starts afresh from a low-entropy state; DSH confines irreversible content (business state, external side effects) outside managed boundaries and scopes, keeping the mechanism structure (coordinates, deltas, inverses) reversible, with evolution occurring only on unloadable registration deltas. The core architecture is not eroded—this is the only feasible way to "manage" entropy increase.
From this, we can state the informatic essence of "reversibility": reversibility = information traceability + information separability. In DSH, traceability means every delta carries its own inverse; the runtime records "who added what and where the inverse is," ready to enumerate and execute in reverse at any time; separability—deltas are registered with fiber/scope as boundaries; any part can be entirely removed without affecting others. Together, these provide an operational version of the separation of concerns that software engineering has long struggled to articulate: to what degree of separation does separation count as genuine? Reversible computation's answer: separation to the degree of reversibility. Every added feature must have a paired undo mechanism; every piece of information input into the system must have an automatic reverse-extraction method. Separation is good or bad depending on whether it can be completely taken away.
Another engineering value of reversibility is that it composes: if every local part is reversible and the relationships between local parts are also reversible, then the whole is reversible—stepwise construction of large reversible systems therefore becomes possible, just as individual Commands with execute/undo can compose into a BatchCommand that remains reversible as a whole. DSH's reversibility is composed in exactly this manner: each plugin is reversible within its constrained scope (unload = reverse-execute all of its own inverses); plugins combine with each other only through declarative interfaces (service/inject, event coordinates); the rules of combination themselves (activation ordering, unload guards) are guaranteed by the runtime—thus the whole of any plugin combination is reversible; install as much as you like, uninstall as much as you like, no residue remains. This is precisely the mechanistic foundation for "effectively controlling the boundary of system complexity": complexity growth is confined to separable, reversible delta layers rather than diffusing throughout global state; the system can bear more combinations because every combination rests on a reversible and determinate foundation.
Entropy increase cannot be avoided, but the location of entropy increase—and the boundary of reversibility—can be designed. Software evolvability has never been about "no constraints" but about placing constraints in the right positions—which layers are reversible, which layers permit irreversibility, and what to do when boundaries are crossed. DSH's coordinates, deltas, inverses, scopes, boundaries, and observational equivalence all answer these questions; it transforms this "art of constraint" from empirical rules into a formalizable, verifiable runtime mechanism. Returning to the opening question—is reversibility over-engineering? It is not rhetoric invented for the "dynamic update" scenario but a path pointed out for software evolution along the law of entropy increase: since the accumulation of chaos cannot be eliminated, make every change reversible and concentrate the irreversible parts at the boundaries—this is the logic behind all the mechanistic analysis in this article.
6. Conclusion
With the analytical framework of reversible computation theory, we can step beyond specific business requirements and see clearly from the theoretical level why DSH's design works, what the essential difference is between it and extensible frameworks like Pi Agent, and in which directions it might be extended in the future. For the Nop platform, its reversible computation mechanism has previously focused primarily on compile-time model construction; a plugin mechanism was planned for runtime but had not been designed in detail. Cordis's design precisely fills this gap. In the future, similar mechanisms will be introduced into the Nop platform to further enhance its dynamic runtime composition capabilities.
References
- Reversible Computation: A Next-Generation Software Construction Theory: An overview of reversible computation theory, explaining its basic principles, core formula, and its differences from the two traditional computational worldviews—Turing machines and Lambda calculus—the theoretical framework underlying this article.
- Generalized Reversible Computation: A Vindication and Elucidation of a Software Construction Paradigm: Vindicates "Generalized Reversible Computation" (GRC) and elucidates its core idea—treating "delta" as a first-class citizen to systematically manage reversibility and irreversibility in software construction processes.
-
A Quick Overview of (Generalized) Reversible Computation Theory: A New Paradigm Unifying Software Construction and Evolution: A rapid overview of (Generalized) Reversible Computation theory, summarizing the core formula
App = Delta x-extends Generator<DSL>and key technical implementations. - A Programmer's Analysis of Reversible Computation Theory: The article cited in Section 4.1, arguing core propositions such as "the independent existence of deltas implicitly requires that the original system possess an explicit coordinate system."
- A Programmer's Analysis of Reversible Computation Theory: Addendum: Supplementary remarks to the Analysis article.
- A Programmer's Analysis of the Delta Concept, with Git and Docker as Examples: A plain-language explanation of the delta concept and coordinate stability—Git's line numbers are unstable coordinates; Docker's file paths are stable coordinates; the coordinate argument in Section 4.1 of this article derives from this.
- What Does "Reversible" in Reversible Computation Theory Actually Mean?: Clarifies the different meanings of "reversible" in multiple contexts, directly corresponding to Section 2's "Delta can be positive or negative" and Section 4.8's "observational equivalence" in this article.
- Delta Oriented Programming from the Perspective of Reversible Computation: Compares reversible computation with academic Feature-Oriented Programming (FOP) and Delta-Oriented Programming (DOP), highlighting the value of the "field" and "coordinate system" concepts—corresponding to this article's discussion of "everything is a plugin = everything is a delta."
- Kustomize from the Perspective of Reversible Computation: A reversible computation analysis of Kustomize's patch mechanism—one of the existing analyses cited at the beginning of Section 4.
- The Essence of React from the Perspective of React Hooks: A reversible computation analysis of React's essence—one of the existing analyses cited at the beginning of Section 4.
- NopTaskFlow: A Next-Generation Logic Orchestration Engine Written from Scratch: The article cited in Section 4.1, containing a deepened discussion of Dijkstra's coordinate system insight (from "assisting understanding" to "supporting computation").
-
The Mathematical Core of Model-Driven Architecture: The Y = F(X) ⊕ Delta Invariant Unifying Generation and Evolution: A mathematical elucidation of the core formula
App = F(X) ⊕ Δof reversible computation—corresponding to Section 4.5's "a plugin's apply function can be viewed as a Generator." - Making Evolution Programmable: XLang and the Structured Paradigm of Reversible Computation: How XLang realizes "programmable evolution" in the structural space—corresponding to this article's discussion of reversibility in the compile-time structural space.
- How to Achieve Customized Development Without Modifying Base Product Source Code: The practical path of Delta-based customized development—corresponding to Section 4.6's discussion of "customizing plugin internals without modifying plugin code."
The DSH paper analyzed in this article: A Programming Paradigm for Spatiotemporal Composability
Top comments (0)