When Causal Emergence Inspires OS3
OS3 started from a very practical engineering intuition:
In a constrained embedded system, causality must remain visible.
An interrupt occurs.
It posts an event.
The event enters a canonical queue.
The event loop dispatches it.
A finite state machine progresses.
Nothing happens outside that chain.
No threads.
No scheduler.
No hidden execution path.
No application-level preemption.
No invisible background work.
At first, this was mostly an engineering discipline. OS3 was designed as a small deterministic event-driven kernel for resource-constrained microcontrollers, initially targeting the CH32V003 RISC-V MCU. The goal was simple: build a system where every observable behavior could be traced back to an explicit event and an explicit transition.
Then I read two papers by Erik Hoel and his collaborators:
- Erik P. Hoel, Larissa Albantakis, Giulio Tononi, “Quantifying Causal Emergence Shows That Macro Can Beat Micro”, PNAS, 2013.
- Brennan Klein, Erik Hoel, “The Emergence of Informative Higher Scales in Complex Networks”, arXiv:1907.03902v2, 2020.
Neither paper is about embedded systems.
Neither paper is about operating systems.
Neither paper is about RISC-V, interrupt handlers, LoRa modems, or finite state machines written in assembly.
And yet, reading them changed the way I reason about OS3.
Not because they provide a software architecture ready to implement, but because they give a precise language for something I was already trying to preserve: causal structure.
1. OS3 before Hoel: preserving causal paths
The first architectural constitution of OS3 was built around a strict event discipline.
The canonical execution path was:
ISR → event_enqueue → event_loop → dispatch → fsm_step / handler
This is not a convention.
It is a constitutional rule.
An interrupt service routine does not run protocol logic.
It does not modify FSM state.
It does not perform complex I/O.
It acknowledges the source, captures the minimum required data, and posts an event.
A finite state machine does not mutate its state from arbitrary action code.
State progression is centralized through fsm_step.
A driver does not contain domain logic.
A domain FSM does not touch hardware directly.
A layer does not bypass the layer below it.
The initial OS3 principles were therefore already centered on ideas such as:
- Event Supremacy
- Interrupt Minimalism
- FSM State Integrity
- Non-Bypassability
- Layered Sovereignty
- Temporal Coherence
In simpler terms:
If a cause produces an effect, the causal path must remain visible.
This already gives OS3 a strong architectural discipline.
But something was missing.
The constitution was saying:
Preserve causality.
It did not yet say:
At which scale is the causal structure most informative?
That question is where Hoel’s work became deeply useful.
2. Effective Information: causality is not just determinism
In Quantifying Causal Emergence Shows That Macro Can Beat Micro, Hoel, Albantakis and Tononi introduce a measure called Effective Information (EI).
For software architecture, the most important idea can be summarized as follows:
A system is causally informative when causes produce effects in a way that is both:
- deterministic — a cause reliably produces an effect;
- non-degenerate — distinct causes do not all collapse into the same indistinguishable effect.
Hoel’s paper decomposes causal effectiveness through two opposite tendencies:
effectiveness = determinism − degeneracy
This distinction is extremely important.
A system can be perfectly deterministic and still be causally poor.
Consider:
A → X
B → X
C → X
D → X
There is no noise.
Each cause reliably produces an effect.
But all causes produce the same effect.
The effect X tells us almost nothing about which cause occurred.
This is degeneracy.
For software architecture, this is immediately recognizable.
Examples:
many errors → ERR
many commands → dispatch_blob()
many deadlines → EV_TIMER
many states → implicit flags
many transitions → same opaque handler
All of these patterns may be fast.
All of them may be deterministic.
But they collapse distinct causes into indistinguishable structures.
They reduce causal clarity.
This is the first major lesson I took from Hoel:
Determinism is necessary, but not sufficient.
A good architecture must also resist degeneracy.
3. Macro can beat micro
The central claim of Hoel’s 2013 paper is captured directly in its title:
Macro can beat micro.
This does not mean that the micro level is unreal.
It does not mean that details are irrelevant.
It means that a macro-level description can sometimes be more causally informative than the raw micro-level description.
Why?
Because the macro level can:
- reduce noise;
- reduce degeneracy;
- make transitions more selective;
- expose mechanisms that are obscure at the micro level;
- represent the system at the scale where its causal structure is most effective.
This is a strong idea for software architecture.
We often judge abstractions using poor criteria:
Is it simpler?
Is it smaller?
Is it faster?
Is it more elegant?
Is it more generic?
Hoel suggests a better question:
Does this abstraction increase causal effectiveness?
In other words:
Does it make the cause-effect structure more explicit,
more deterministic,
and less degenerate?
This distinction matters a lot.
A bad abstraction hides causality.
A good abstraction reveals it.
A bad macro-level object is a fog machine.
A good macro-level object is a lens.
4. Klein & Hoel: informative higher scales in networks
The 2020 paper by Brennan Klein and Erik Hoel extends the discussion to complex networks.
They define the Effective Information of a network as:
EI = H(<Wout_i>) − <H(Wout_i)>
The first term captures how certainty is distributed globally across the network.
The second term captures the average uncertainty in each node’s outgoing relationships.
In practical terms:
- if each node has many uncertain outputs, the network contains local noise;
- if many nodes all point to the same destination, the network becomes globally degenerate;
- high-EI networks contain more certain, more specific relationships.
This immediately maps to software systems.
A software architecture can be viewed as a graph:
- states are nodes;
- events are edges;
- handlers are transition mechanisms;
- dispatchers are routing structures;
- layers define allowed paths;
- timers define temporal transitions.
From that perspective, some architectural smells become information-theoretic smells.
A huge dispatch block is a hub.
A generic error path is a degenerate sink.
A timer system where many distinct causes become the same EV_TIMER is a loss of causal information.
A state machine whose real behavior is hidden in flags is a low-clarity network.
Klein and Hoel’s network formulation made one thing clear to me:
Causal structure can be audited as a graph.
That is an extremely practical idea for OS3.
5. From theory to engineering invariants
The interesting step is not to “apply” Hoel mechanically.
The interesting step is to ask:
If these papers are right, what engineering invariants can be extracted from them?
The papers do not say how to write embedded software.
They do not prescribe FSMs.
They do not tell us to avoid schedulers.
They do not define interrupt minimalism.
But they do provide concepts that can be translated into architectural rules.
The translation I arrived at is the following.
5.1 Determinism becomes a software invariant
If the same state receives the same event, the resulting transition should be structurally predictable.
In OS3 this supports:
- table-driven FSMs;
- explicit states;
- bounded handlers;
- no hidden background execution;
- no implicit state mutation.
The rule becomes:
(state, event) → next_state / action
not:
(state, event, hidden flags, global mode, previous timing accident) → maybe something
This reinforces the existing FSM discipline.
5.2 Degeneracy becomes an architectural smell
Before reading Hoel, I already disliked large opaque dispatch logic.
After reading Hoel, I had a sharper reason.
A monolithic dispatch block is not merely ugly.
It can be causally degenerate.
In software, degeneracy often appears when many distinct causes are routed through the same opaque structure:
many commands → one dispatch blob
many errors → one generic error
many deadlines → one generic timer event
many modes → one implicit flag soup
The problem is not necessarily that these structures are slow.
The problem is not even that they are always wrong.
The problem is that they collapse causal variety into an indistinguishable mechanism.
The design question becomes:
Can the system still distinguish the causes that matter?
If not, the architecture has lost information.
5.3 Distinguishability becomes mandatory
One of the strongest principles derived from Hoel is this:
Distinct causes must remain distinguishable.
This has consequences everywhere.
Bad:
CARD_DECLINED → false
OUT_OF_STOCK → false
TIMEOUT → false
UNKNOWN → false
Better:
CARD_DECLINED → error.card_declined
OUT_OF_STOCK → error.out_of_stock
TIMEOUT → error.timeout
UNKNOWN → error.unknown
In OS3 terminology:
many causes → one opaque state
is suspicious.
Convergence is not always wrong.
But convergence must be explicit and justified.
A system is allowed to collapse multiple paths into one state only if the lost distinctions no longer matter for future behavior, observability, or proof obligations.
5.4 The optimal scale becomes a design question
Hoel’s “macro can beat micro” directly affects how one should think about abstraction.
The question is not:
Should this be micro or macro?
The question is:
At which scale is the mechanism most causally explicit?
This matters for OS3 because the project is built from small, explicit mechanisms.
There is a temptation, especially in embedded systems, to assume that lower-level is always better.
But lower-level is not automatically clearer.
A collection of scattered micro-handlers may be less understandable than one explicit macro-FSM.
A sequence of unrelated timers may be less informative than one timer group representing protocol phases.
A set of string comparisons may be less causal than a declarative command table.
The right abstraction is not the smallest abstraction.
The right abstraction is the one that maximizes causal effectiveness.
5.5 Temporal grain becomes part of architecture
Hoel’s 2013 paper also discusses temporal emergence.
That matters because time is not merely a runtime concern.
In embedded systems, time is part of the causal structure.
A timer is not only a delay.
It is a statement that something should become causally relevant at a particular moment.
If several deadlines are actually phases of the same mechanism, treating them as unrelated micro-deadlines may obscure the causal structure.
The architecture must therefore ask:
Is this a set of independent timers,
or one temporal mechanism observed at too fine a grain?
This question directly influenced the OS3 timer model.
6. Influence on the OS3 Constitution
These reflections led to a revision of the OS3 Constitution.
The earlier constitution was mostly centered on temporal discipline and event non-bypassability.
The revised multi-architecture constitution now includes additional principles inspired by Hoel’s work.
The most important additions are the following.
6.1 Optimal Grain Requirement
The constitution now states:
Each subsystem must operate at the abstraction scale
that maximizes causal effectiveness.
This is the direct translation of “macro can beat micro” into an OS3 design rule.
A macro abstraction is valid only if it:
- increases determinism;
- reduces degeneracy;
- preserves explicit transitions.
This prevents two opposite errors:
- hiding everything inside large opaque abstractions;
- fragmenting everything into micro-pieces that obscure the real mechanism.
6.2 Causal Effectiveness
The constitution now explicitly defines:
Causal Effectiveness = Determinism − Degeneracy
This is not a runtime metric yet.
It is first a design criterion.
A subsystem is not judged only by latency, size, or elegance.
It is also judged by how clearly it preserves the relation between causes and effects.
6.3 Distinguishability
The constitution now requires:
Distinct causes must remain distinguishable within system representation.
This is probably the most practical rule.
It immediately targets patterns such as:
- generic errors;
- monolithic dispatch blocks;
- hidden flag combinations;
- indistinguishable timer events;
- overly broad handler states.
This is where Hoel becomes very concrete.
6.4 Degeneracy Audit Proof
The revised proof obligations include a degeneracy audit.
A change must be able to show that:
- distinct causes are not collapsed invisibly;
- convergence points are intentional;
- causal distinctions required for future behavior are preserved;
- abstractions do not erase relevant mechanisms.
This is a new kind of software review criterion.
It is not about code style.
It is not about formatting.
It is not even primarily about performance.
It is about preserving causal information.
7. What this changed in practice
The influence of Hoel’s papers on OS3 is not merely rhetorical.
They changed the kind of questions I ask while reviewing the kernel.
Before:
Is this handler bounded?
Does this path bypass the queue?
Does this ISR remain short?
Is state written only by fsm_step?
After:
Are distinct causes still distinguishable?
Is this abstraction hiding causality or revealing it?
Is this dispatch structure degenerate?
Is this timer event too generic?
Is this FSM too micro or too macro?
Does this level maximize causal effectiveness?
That last question is the important one.
The papers made it possible to move from:
This feels architecturally wrong.
to:
This reduces causal effectiveness because it increases degeneracy.
That is a major improvement.
8. Case study: command parsing
A concrete example is the UART console.
A minimal console FSM can easily become deceptive.
At the formal FSM level, it may have only two states:
INIT
IDLE
But in the action code, there may be a large command parser.
That parser contains the real causal structure of the component:
"stats" → print statistics
"lora" → print LoRa state
"tx" → enqueue LoRa TX
"rx" → enqueue LoRa RX
If this structure is hidden in a cascade of comparisons, the FSM table is formally true but causally incomplete.
The causal variety exists.
But it is hidden inside a blob.
The Hoel-inspired refactoring is to move toward a declarative command table:
command_string → command_action
For example:
"tx" → EV_SOFT_LORA_TX
"rx" → EV_SOFT_LORA_RX
"stats" → action_stats
"help" → action_help
Not because tables are prettier.
Because they preserve causal distinguishability.
The command becomes a cause.
The event becomes its explicit effect.
The mapping becomes auditable.
That is the difference between syntax and mechanism.
9. Case study: LoRa temporal phases
Another example is LoRa.
A LoRa exchange is not merely a collection of deadlines.
It has temporal phases.
A raw micro-timer representation may consume several timer IDs and produce generic timer events:
timer 1 → RX1 open
timer 2 → RX1 close
timer 3 → RX2 open
timer 4 → final timeout
This works.
But it may fail to represent the actual mechanism.
In many radio protocols, such deadlines are not independent.
They are phases of one causal temporal object:
TX done
→ RX1 window
→ RX2 window
→ timeout / retry
This suggests the concept of a timer group.
A timer group is not a scheduler.
It does not run protocol logic in the interrupt.
It does not bypass the event queue.
It is simply a way of representing related temporal phases as one macro-timer structure.
The ISR remains minimal:
acknowledge → enqueue timer event → return
The timer service later unfolds the group into semantic events:
EV_LORA_RX1_OPEN
EV_LORA_RX1_CLOSE
EV_LORA_RX2_OPEN
EV_LORA_TIMEOUT
The timer service does not decide LoRa behavior.
It only preserves temporal structure.
The LoRa FSM remains the owner of LoRa logic.
This preserves OS3’s constitutional rule:
ISR does not interpret time.
The timer service structures time.
The FSM interprets events.
This is the kind of design that Hoel’s temporal emergence makes easier to justify.
10. Case study: macro-FSMs
Another consequence is the legitimacy of macro-FSMs.
In a strict low-level embedded culture, one may be tempted to split everything into very small handlers.
But excessive micro-fragmentation can produce a graph that is harder to reason about.
If several micro-FSMs are tightly coupled and constantly exchange events, they may actually represent one higher-level mechanism.
In that case, grouping them into a macro-FSM may increase causal effectiveness.
The criterion is not taste.
The criterion is:
Does the grouping make transitions more deterministic
and less degenerate?
If yes, the macro-FSM is not an abstraction leak.
It is the correct causal scale.
This is the most important shift brought by Hoel’s work:
micro is not automatically more truthful.
macro is not automatically more artificial.
The right scale is the one where the mechanism becomes most explicit.
11. What Hoel does not give us
It is important not to overstate the case.
Hoel’s papers do not prove that OS3 is correct.
They do not prove that event-driven kernels are superior to schedulers.
They do not provide a direct software engineering methodology.
They do not eliminate the need for benchmarking, testing, formal proof, or careful embedded engineering.
The translation from causal emergence to software architecture is an extrapolation.
But it is a productive extrapolation.
The papers provide a rigorous conceptual framework.
OS3 provides a concrete engineering domain where that framework can be tested as a design discipline.
That is the interesting point.
12. Toward causal software engineering
The broader idea is that software architecture should not only be evaluated through traditional metrics:
latency
throughput
memory usage
code size
API simplicity
test coverage
These matter.
But they do not fully capture architectural quality.
A system may be fast and still causally opaque.
A system may be small and still degenerate.
A system may be modular and still hide the real mechanism.
Causal emergence suggests another axis:
Does the architecture preserve causal information?
For OS3, this becomes a design discipline:
- make transitions explicit;
- avoid hidden execution;
- distinguish causes;
- audit degeneracy;
- choose the right abstraction scale;
- treat time as a causal structure;
- reject shortcuts that hide cause-effect paths.
This is why Hoel’s work has been so inspiring for OS3.
It gave a language to something that was initially only an engineering instinct.
13. Conclusion
OS3 began as a deterministic event-driven kernel for very small embedded systems.
The guiding intuition was simple:
causality must remain visible.
Reading Erik Hoel’s work on causal emergence sharpened that intuition.
The key lesson is not that macro abstractions are good.
Nor that micro descriptions are bad.
The lesson is more precise:
the correct representation is the one that maximizes causal effectiveness.
For OS3, this changed the constitution.
It introduced:
- causal effectiveness;
- distinguishability;
- degeneracy audits;
- optimal grain;
- temporal abstraction as a legitimate design question.
The result is a stronger architectural discipline.
OS3 no longer merely says:
Everything must pass through events.
It now also asks:
At which scale does this event structure become most informative?
That question may turn out to be one of the most important design questions in the entire project.
References
[1] Erik P. Hoel, Larissa Albantakis, Giulio Tononi,
“Quantifying Causal Emergence Shows That Macro Can Beat Micro”,
Proceedings of the National Academy of Sciences, vol. 110, no. 49, pp. 19790–19795, 2013.
DOI: 10.1073/pnas.1314922110
[2] Brennan Klein, Erik Hoel,
“The Emergence of Informative Higher Scales in Complex Networks”,
arXiv: 1907.03902v2, 2020.