Contexts, transformations, inverses, and dependency predicates
By the end of this lesson, you should be able to explain the first formal model in plain language and connect every symbol to a concrete value and function call.
× is the product, or pair, type constructor. A × B means a pair whose first component has type A and whose second component has type B. It is the same idea Lesson 00 wrote as (A, B); it does not mean multiplication.
You will also be able to explain why a reversible effect is one local building block for a component that can be unloaded, and why safe unloading additionally needs tracking, ordering, dependency lifecycle rules, and assumptions about what "restored" means. The companion coeffect predicate gives one way to describe when a component's requirements are satisfied.
Complete Lesson 00: Values, Types, and Function Calls first if formal notation is new. The source paper is optional background; this course will teach every prerequisite and restate every definition we need.
DeepSeek Harness presents the agent as a composition of replaceable capabilities: models, tools, skills, sessions, sandboxes, storage, loops, scheduling, and UI. Its Cordis kernel manages mounting, unmounting, and dependencies. The official overview calls this "everything is a plugin." The architecture documentation makes the same idea concrete: plugins contribute services, typed events, and reversible effects to a shared context, and the agent loop itself is a plugin. [Harness architecture]
The paper asks what semantics such a system needs if components can arrive and depart while the process continues running. It splits the problem into two dimensions:
The paper's title, A Programming Paradigm for Spatiotemporal Composability, names exactly this combination. The paper is an actively revised preprint, so exact theorem numbering may change in later versions. [Cordis paper repository]
Let Γ be a set of possible runtime contexts. The Greek letter is not magic. It simply names the space of states the component can observe or modify.
For a toy example, let the context be a set of registered capabilities:
Here ∅ is the same empty set as the earlier toy value empty = {}.
Here, γ₀, γ₁, and γ₂ are individual context states. The uppercase Γ is the whole set of possible states. This distinction matters:
Γ means "the type or space of contexts."γ means "one particular context value."γ₀ usually means the state before a sequence of effects.A context transformation is an ordinary function:
It consumes one state and returns another. If f registers a model:
Function composition is written f ∘ g. The paper uses the conventional mathematical direction in which the right function acts first:
So f_model ∘ f_tools means: register tools first, then register the model.
When you see a function such as f : Γ → Γ, read it as "one possible way of changing the runtime context." Do not yet think about agents. This is ordinary state-transformer mathematics.
A normal state transformation tells us only what happens next. It does not tell us how to undo what happened. In this lesson, we model a reversible effect by returning a possible recovery function alongside the new state.
This line is only a shape label. It does not show a current state yet.
In the paper's symbols, State is Γ, an actual state such as empty is γ, and Undo is Γ → Γ. Therefore:
So the current state γ is not missing. It belongs to the second line, where we actually call the effect. The first line only says what kind of function e is.
It would be misleading to rewrite the type as e : Γ → (new_state, inverse_function). That is not a new type equation: new_state and inverse_function are names for returned values, not types.
The correct sequence is:
So we do not replace Γ × (Γ → Γ) with the names. We use the product type to say what types the two returned values must have, then we give those concrete returned values names after calling e.
γ : Γ.(δ, g).δ : Γ is the new state.g : Γ → Γ is a returned function. It is called an inverse only when it satisfies an additional behavioral obligation for this execution.The raw type e : EΓ guarantees only the return shape. It does not prove that the returned function actually restores anything. For a particular input, if:
then this particular application has a witnessed, state-specific recovery function. The second equation is an additional obligation; it is not encoded by the raw type alone.
Keep these two constructions separate:
Validity obligation for the fixed pair: g(f(γ)) = γ on the stated domain. If it must hold for every γ, write g ∘ f = idΓ.
In the fixed-pair model, one named g accompanies one named f. In the returned-effect model, the effect may return a different function g_γ for different inputs. Even there, the returned function is only constrained at the particular output δ unless a stronger universal law is separately proved.
Define the forward and recovery functions:
Now consider one particular starting state where the model key is absent:
This is one witnessed application. The raw function e_model(γ) = (f_model(γ), g_model) is total as an element of EΓ, because it returns a state and a function-shaped value for every γ. However, the witness equation g_model(f_model(γ)) = γ holds only when "model" ∉ γ. If the key was already present, blindly removing it could delete an older registration rather than restore the caller's exact state.
An implementation could retain g_model and invoke it when the corresponding registration is unloaded. That is an implementation pattern built on this abstraction, not a guarantee provided by the type alone.
The code that applies or constructs the effect supplies the inverse or disposer. In a plugin system this is often component code, while Cordis tracks it when the registration uses its effect API. The runtime cannot generally prove that a user-written inverse restores the intended real-world state. If the effect sends an irreversible external message, deleting a local registration does not unsend that message.
Suppose a component performs two effects in sequence:
To return to γ₀, we must apply g₂ first, then g₁:
This is why the paper defines twisted composition of effect pairs as:
The pair-composition operator uses the same right-to-left convention as ordinary function composition. Thus (f₁,g₁) ∘ (f₂,g₂) represents f₂ running first, followed by f₁. If the execution order is f₁ then f₂, write:
The inverse functions therefore appear in the order needed for recovery: g₂ runs before g₁. The forward transformations and inverse transformations are not being read in the same chronological direction.
This behaves like a LIFO cleanup stack in the example below, but the formal accumulator is a composed function, not literally a stack data structure:
The paper reifies the cleanup stack as part of the state itself. Given a context Γ, define the effect context:
An element of ∂Γ is a pair:
γ is the current context.φ is the accumulated recovery function.The initial effect context is:
where idΓ(γ) = γ. Initially, there is nothing to undo.
Tracking an effect with a forward function f and an inverse g gives the following rule. Let φ : Γ → Γ be the accumulated recovery function, the second component of ∂Γ = Γ × (Γ → Γ). For γ : Γ:
The state moves forward. The accumulator grows by composing the new inverse on the right. Given the paper's convention for composition, this means the newest inverse runs first when φ is later applied.
Let "A" and "B" be distinct string keys. For a registry state S : Γ, define the installation functions and their candidate recoveries:
Assume "A" and "B" are absent initially. Start with an empty registry and install "A", then "B":
Now recover:
The accumulator has turned two toy cleanup actions into one composed recovery function. This example does not yet establish the paper's general notion of effect independence; it only shows that these two set-removal functions commute in this toy model.
The recovery operation itself is:
It applies the accumulated inverse to the current state and resets the accumulator. More precisely, if the sequence starts at (γ₀, idΓ), each application has the form eᵢ(γᵢ₋₁) = (γᵢ, gᵢ), and each witness satisfies gᵢ(γᵢ) = γᵢ₋₁, then recovery after the whole sequence returns (γ₀, idΓ) in this idealized model. This assumes the sequence contains only these effects and recovery is applied to the state they produced. Out-of-order removal or interleaving with other components requires separately stated independence conditions.
Effects describe what a component does. Coeffects describe what it needs.
Let:
Here Σ is the type/space of finite partial environments, and ⇀ means "partial function": an environment may lack a binding for some keys.
A partial environment may lack some keys. Formally, dom(σ) is the set of keys for which σ has a value, and:
The symbol ⇀, when used for a map, means "partial function": the map is not required to have an answer for every possible key. We can therefore think of σ as a typed lookup table:
A lookup such as get("model", σ) is defined only when "model" ∈ dom(σ). The values are omitted from the small domain diagrams below; in diagrams, bare labels such as model and tools abbreviate the corresponding string keys "model" and "tools". A displayed set such as {model, tools} abbreviates dom(σ) = {"model", "tools"}.
A component declares a specification d ⊆ K. A displayed set in an environment diagram, such as σ = {model, tools}, abbreviates dom(σ) = {"model", "tools"}. When the braces follow d =, they denote the dependency-key set itself. The classifier below is about this simplified presence-only model. The specification is satisfied exactly when every declared key is present:
Read σ ⊧ d as "the environment state σ satisfies dependency specification d." Here ∀ means "for every," ∧ means "and," and σ ̸⊧ d means "σ does not satisfy d."
For this dependency-only classifier, take σ : Σ and σ′ : Σ as two environment snapshots. Let a separate environment update be u : Σ → Σ with u(σ) = σ′. Do not identify u with the earlier context transformation f : Γ → Γ unless the model explicitly defines Γ = Σ. The prime mark means "after the change," and σ ̸⊧ d means "σ does not satisfy d."
This is the mathematical seed of reactivity. In this simplified dependency-only model, the classifier reacts when the satisfaction truth value changes. It is not the complete Harness lifecycle rule: provider identity, target changes, lifecycle state, isolation, and other runtime conditions may matter even when σ ⊧ d remains true.
Use this running translation:
| Formal object | Harness interpretation |
|---|---|
Γ | Type or space of possible context states |
e : EΓ | Abstract model of a capability installation returning a candidate recovery function; the type alone does not guarantee successful disposal |
φ | Accumulated plugin cleanup |
k | Service key such as model or tools |
σ | Currently provided services |
d | Component's declared dependencies |
σ ⊧ d | All declared dependency keys are present; this is a prerequisite for activation |
Later lessons will add the hard parts that are intentionally absent here: provider identity, observational equivalence, isolation, interception, fibers, asynchronous transition rules, and the theorems that lift local reasoning to an interleaved system.
Try these without scrolling back. The buttons reveal model answers after you commit to an answer mentally.
1. What does e : EΓ return when applied to γ?
e(γ) = (δ, g) and the additional recovery obligation is g(δ) = γ.2. Why is the inverse order reversed?
f₁ runs before f₂, then g₂ must remove the later change before g₁ can remove the earlier change.3. Let d = {model, tools}. If σ contains only model, is the component active?
σ ̸⊧ d because tools is absent. Adding tools creates an activating transition.4. What is the difference between Γ and γ?
Γ is the space or type of possible contexts. γ is one concrete context state inside that space.If the type Γ → Γ × (Γ → Γ) is still visually dense, rewrite it in English: "given a state, return a new state and a function that undoes this application." That sentence is the definition. The symbols are compression, not additional magic.