Chapter 13
The Olai lens
The framing in cordis-for-olai.md: Olai's plugin system is a registry — values filtered once at startup, composed into a PluginServices blob, immutable after. Cordis is a runtime — functions installing revertible effects into a shared context, activation and deactivation driven by declared dependencies, hot reload. Design judgments kept: vault authority over plugins, write-only doorbell, plugin-owned error messages, browser receives answers not rules, the fence (no cycles; no importing @olai/plugin-api). Limitations overturned: compiled-in only → loader mounts by name from config rows; no plugin-to-plugin deps → inject with PENDING; enable/disable CLI-only → a disabled row field; privileged core → driver plugins in an orchestrate profile.
| Paper concept | Olai design point | Chapter |
|---|---|---|
| Revertible effects | Every registration carries a disposer; LIFO unwind; no uninstall path to write. Spike: disabled rows dispose cleanly; a throwing apply → FAILED, siblings unaffected (exactly §4.4's failure semantics). | 4, 9.1 |
| Reactive coeffects | inject lists; absent provider = wait, not error; provider replacement reloads only dependents. | 5 |
| Interception | Subtree write fence as metadata; vault precedence = right-biased ⊕; readable without reload. | 5.4 |
| Isolation realms | Node-agent scopes: same key, different binding per agent subtree; loader-managed realms move with the entry. | 5.3, 11.1 |
| Confluence (Thm 80) | "Profiles differ only by row selection" is sound because the quiescent state is a function of the final config, not the load/unload history. The orchestrator-as-driver-group idea rests on this. | 9 |
| Ordering (Thm 70) + guard | No boot order; fetch modules concurrently; activate when satisfied. | 8.4, 9 |
| Commutative keys | The live sibling registry: tagged set → incremental add/drop as commuting effects; ordered → carry order in a coeffect. | 7 |
| Nominal keys + peer deps | Already where the doc lands; the paper flags it as an open gap, and npins hydration makes the peer-range check yours to run. | 12.2 |
| HMR | Deferred on Bun; costs no guarantee. | 11.2 |
13.1 Two runtimes: Effect and Cordis
The doc's split — Cordis controls what mounts when, Effect controls how it works — is exactly the seam the paper draws against Effect-TS in §7.1: (a) Effect tracks only inside the monadic type, Cordis is an overlay; (b) Effect's Scope / acquireRelease is structured, and a withdrawn service doesn't undo what it did, whereas Cordis pairs each effect with an inverse and re-resolves requirements as providers come and go. So the effect-cordis bridge (Phase 4) is putting Effect's acquireRelease inside a Cordis fiber's lifetime: Cordis opens and closes the scope; Effect owns what happens inside. Requirement channels R look like inject, but Effect resolves R once at provide; the reactive part (PENDING → ACTIVE → UNLOADING on provider change) has no Effect counterpart and stays on the Cordis side.
13.2 Questions the paper makes sharp
- Is every shared mutable location in Olai reified at a key? Anything a plugin touches outside Σ is outside the guarantees (Def 56, §6.1).
- Which Olai keys are commutative? Roster: yes if tagged. Hook chains: no — waterfall order is a coeffect.
- Where is the system boundary? Vault files on disk are emissions (other programs write them); git commits are compensable, not revertible. What does "dispose" mean for a plugin that has already written a row?
- Confluence excludes FAILED fibers. How does Olai surface "this plugin failed under this schedule" to the user? Plugin-owned error prose fits here.
- Provider replacement under exclusive binding reloads every dependent. For chat/vault services under node-agents, is a broker (§6.2) the right shape so agents don't bounce when a backend swaps?