Chapter 10

Cordis: reading the source

Core is about 1,900 lines in packages/core/src/: context.ts (78), fiber.ts (488), reflect.ts (283), registry.ts (214), service.ts (80), events.ts (188), logger.ts, utils.ts. Line numbers below are from cordiverse/cordis master as of 2026-09-03. The paper's Table 2 names differ a little from the code:

TheoryPaper (§5)Code
effectΓ, ℑΓctx.effect(cb)Fiber.effectfiber.ts:275–340
set(k,v) / get(k)ctx.set / ctx.getReflectService.provide / getreflect.ts:177, 152
isolate, interceptctx.isolate, ctx.interceptcontext.ts:65–77, both via extend
O-Insert + O-Retirectx.usectx.pluginregistry.ts:193, fiber.ts:170–199
d / pfiber.inject / providesame
ω committed viewfiber.committedfiber.store
target_nfiber.targetrunner.epoch — a string ':uid:uid…', '__INACTIVE__' for ⊥
θfiber.stateFiberState: PENDING (Inactive), LOADING (Reloading), ACTIVE, FAILED, DISPOSED, UNLOADING
transition in flightfiber.inertiasame (a Promise)

10.1 Effect tracking — Fiber.effect

// fiber.ts (abridged)
effect(execute: () => Effect, label = 'anonymous'): any {
  this.assertActive()
  const disposables: Disposable[] = []
  const dispose = () => {
    let task
    for (const dispose of disposables.splice(0).reverse()) {   // LIFO
      task = task ? task.then(dispose) : dispose()
    }
    return task
  }
  const runner = { execute, epoch: true, collect: d => disposables.push(d), ... }
  let task = this._execute(runner)          // drives sync/async iterators, collecting yields
  const wrapper = () => { if (!runner.epoch) return; runner.epoch = false; return task ? task.then(dispose) : dispose() }
  disposables.push(this._disposables.push(wrapper))   // child's inverse is an effect on the parent (∂²Γ)
  return wrapper
}

_execute (:229–273) accepts a disposer, an iterable of disposers, a promise of one, or an async iterable — the 𝔈Γ/ℑΓ ad-hoc polymorphism of §5.1.1. In the async-iterable branch it checks runner.epoch !== oldEpoch before each iter.next() — the step-boundary interruption. Flipping epoch makes dispose fire at most once ("firing twice would apply an inverse at a state no application of the effect produced").

Unchecked

That the disposer actually reverts is the author's obligation (the 𝔈Γ* witness). §5.1.1 says so in as many words; the runtime verifies nothing. Likewise the commutativity witness of a key.

10.2 Coeffects — ReflectService

// reflect.ts (abridged)
provide(name, value?, check?) {
  return this.ctx.fiber.effect(() => {                    // set(k,v) IS an effect
    this.ctx.root[symbols.isolate][name] ??= Symbol(name)  // realm symbol ρ(k)
    const key = this.ctx[symbols.isolate][name]
    const impl = { name, value, fiber: this.ctx.fiber, check }
    if (this.store[key]) throw new Error(`service "${name}" has been registered at <…>`)  // k ∉ dom σ
    this.store[key] = impl; this.ctx.fiber.store![name] = impl
    if (this.ctx.fiber.state === FiberState.ACTIVE) this.notify([name])
    return async () => {
      delete this.store[key]
      const fibers = this.notify([name])
      await Promise.allSettled(fibers.map(f => f.await()))  // the L-Unload guard: wait for dependents
      delete this.ctx.fiber.store![name]
    }
  }, `ctx.provide(${JSON.stringify(name)})`)
}

notify(names, filter = sameRealm) {                        // Algorithm 3
  for (const runtime of this.ctx.registry.values())
    for (const fiber of runtime.fibers) {
      let hasUpdate = false
      for (const name of names)
        if (name in fiber.inject && filter(fiber.ctx, name)) { hasUpdate = true; fiber._checkImpl(name) }
      if (hasUpdate) { fiber._refresh(); fibers.push(fiber) }
    }
  ...
}

_getImpl(name, strict = true) {
  const impl = this.store[this.ctx[symbols.isolate][name]]
  if (strict && impl.fiber.state !== FiberState.ACTIVE) return   // only ACTIVE fibers provide
  return impl
}

The Proxy get trap (:63–100) is Algorithm 6: walk up the fiber chain; return from the first fiber.store that binds the key; a fiber that declares the key but hasn't committed it throws cannot get required service "x" in inactive context; reaching root without a declaration throws cannot get property "x" without inject. The set trap refuses writes without provide. mixin (:241–267) is how ctx.on/plugin/effect/get/provide… appear on the context — accessors delegating to events, registry, fiber, reflect.

10.3 Isolation and interception — derived, literally

// context.ts
isolate(name: string, label?: symbol) {
  const shadow = Object.create(this[symbols.isolate]); shadow[name] = label ?? Symbol(name)
  return this.extend({ [symbols.isolate]: shadow })
}
intercept(name: string, config: any) {
  const intercept = Object.create(this[symbols.intercept]); intercept[name] = config
  return this.extend({ [symbols.intercept]: intercept })
}
// service.ts — right-biased merge up the prototype chain
[symbols.resolveConfig](base?, head?) {
  let intercept = this.ctx[Context.intercept]; const configs = []
  while (this.name in intercept) {
    if (Object.hasOwn(intercept, this.name)) configs.unshift(intercept[this.name])
    intercept = Object.getPrototypeOf(intercept)
  }
  return this['Config']?.merge ? this['Config'].merge(...configs) : Object.assign({}, ...configs)
}

10.4 Lifecycle — Fiber

// fiber.ts (abridged)
_refresh() {                                   // recompute the target-view digest
  let epoch = ''
  for (const name of Object.keys(this.inject)) {
    const impl = this._store[name]
    if (!impl) { epoch = INACTIVE; break }
    epoch += ':' + impl.fiber.uid               // provider identity, not value
  }
  this._setEpoch(epoch)
}
private _setEpoch(epoch) {
  if (epoch === this._runner.epoch || this._error) return   // FAILED only recovers through update()
  this._runner.epoch = epoch
  if (this.inertia) return                                  // inertial: finish the transition first
  this._updateState(() => epoch !== INACTIVE && oldEpoch === INACTIVE
    ? (this.inertia = this._reload(), FiberState.LOADING)
    : (this.inertia = this._unload(), FiberState.UNLOADING))
}
private async _reload() {
  this.store = { ...this._store }                            // commit the view ω
  const oldEpoch = this._runner.epoch
  try { await this._execute(this._runner) }                 // guard: epoch stable at each boundary
  catch (reason) { this._error = reason; this._runner.epoch = INACTIVE }
  this._updateState(() => this._runner.epoch === oldEpoch
    ? (this.inertia = undefined)                            // ACTIVE
    : (this.inertia = this._unload(), FiberState.UNLOADING)) // target moved: chain into unload
}
private async _unload() {
  await Promise.all(this._disposables.clear().map(dispose => ...))   // run inverses
  this.store = undefined                                     // discard ω last
  this._updateState(() => this._runner.epoch === INACTIVE
    ? (this.inertia = undefined)                            // INACTIVE
    : (this.inertia = this._reload(), FiberState.LOADING))  // chain back into reload
}

Observe the three lines §5.1.3 says carry Thm 70: _reload commits the view first and _unload discards it last (a fiber reads the same bindings for its whole loaded life, teardown included); _updateState marks UNLOADING before the task is created (L-Leave: stop providing before any inverse is scheduled); and provide's disposer awaits the notified dependents (the guard).

10.5 Instantiation is a tracked effect of the parent

// fiber.ts constructor (abridged)
this.dispose = parent.fiber.effect(() => {           // O-Insert as an effect of the parent
  const remove = runtime.fibers.push(this)
  try { this.config = resolveConfig(runtime, config); this._refresh() }
  catch (error) { this._error = error }
  return async () => {                                 // its inverse is O-Retire
    this.uid = null                                    // DISPOSED; a uid is never reused
    ...
    this._setEpoch(INACTIVE)
    while (this.inertia) await this.inertia            // wait for the child's in-flight transition
  }
}, 'ctx.plugin()')

So unloading a parent cascades to children exactly as Def 52 describes. RegistryService.plugin (registry.ts:193–213) accepts a function, a constructor, or an object with apply, and Inject.resolve normalizes inject: ['a','b'] or {a: cfg} to a dict; per-key config from inject is installed as intercept metadata on the child context (fiber.ts:137–144). update(config) (:478) clears _error — retry is a revision.

10.6 Events

EventsService.on registers through fiber.effect (events.ts:134–141): a listener is a revertible effect, and the hooks table is the canonical commutative key (one entry per registration, removed by identity). Dispatch modes: emit (fire-and-forget), parallel (allSettled, AggregateError), serial (await each, stop at first non-null), bail (sync, stop at first non-null), waterfall (middleware with a once-only next()). The internal/* events (plugin, status, service, update, get, set, listener, dispatch) are what the loader and devtools hang off.