$dshubuser@dshub:~/plugins$
  • Plugins
  • Submit
  • Blog
Lifecycle and Effects: Make Cordis Registrations Reversible
2026/08/24

Lifecycle and Effects: Make Cordis Registrations Reversible

Follow a Fiber from PENDING to DISPOSED and see how effects, disposers, and HMR manage plugin side effects together.

The conclusion: a plugin creates resources while loading and runs a disposer while unloading; Fiber binds both actions to one lifecycle.

That is why HMR is not merely importing a file again. The implementation may import the replacement module before unloading the old Fiber; the invariant is that the old Fiber's listeners, services, child plugins, and external resources are reversed before the new Fiber becomes active.

01 / Fiber is the runtime handle for a plugin instance

Cordis Fiber state machine from PENDING through ACTIVE and UNLOADING to DISPOSED

A plugin instance moves through:

PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
                 ↘ FAILED
  • PENDING: declared, but dependencies are not ready.
  • LOADING / ACTIVE: apply is running or complete.
  • FAILED: configuration validation or apply threw.
  • UNLOADING / DISPOSED: cleanup is running or complete.

Fiber is not just a status label. It is the lifecycle handle owned by a plugin instance. fiber.dispose() waits for cleanup and recursively disposes child plugins.

02 / Put external resources inside an effect

Cordis effect ownership connecting plugin load resources to unload disposers

Cordis knows how to reverse its own registrations. Timers, connections, and watchers need an explicit wrapper:

ctx.effect(() => {
  const timer = setInterval(() => console.log('tick'), 200)
  return () => {
    clearInterval(timer)
    console.log('cleaned up')
  }
})

The effect body runs during load; the returned function runs during unload. The plugin does not need a global shutdown function that guesses who owns the resource.

03 / Common registrations are effects too

These operations attach cleanup to the current Fiber:

  • ctx.on() registers an event listener.
  • ctx.plugin() mounts a child plugin.
  • A Service registers a service name.
  • ctx.effect() wraps an external resource Cordis does not manage yet.
Cordis Fiber effect stack showing reverse cleanup for listeners services children and timers

During unload, Fiber collects disposers in reverse registration order and starts top-level cleanup concurrently; asynchronous cleanup from sibling effects may run concurrently, and fiber.dispose() waits for all of it. If two resources require strict ordering, put their cleanup in one effect and await inside it.

04 / A small verifiable example

import type { Context } from '@deepseek-ai/cordis'

function heartbeat(ctx: Context) {
  console.log('heartbeat plugin loading')
  ctx.effect(() => {
    const timer = setInterval(() => console.log('tick'), 200)
    return () => {
      clearInterval(timer)
      console.log('heartbeat cleaned up')
    }
  })
}

export function apply(ctx: Context) {
  const fiber = ctx.plugin(heartbeat)
  ctx.effect(() => {
    const shutdown = setTimeout(async () => {
      await fiber.dispose()
      console.log('disposed')
      process.exit(0)
    }, 700)
    return () => clearTimeout(shutdown)
  })
}

The output shows heartbeat and ticks, followed by cleanup and disposed. The important property is that cleanup has completed before fiber.dispose() resolves.

05 / Effects are HMR infrastructure

HMR can turn a code refresh into a controlled runtime transition: import the replacement module, dispose the old Fiber and reverse its effects, then register and activate the replacement Fiber. Resources without disposers do not disappear by magic, so each plugin should give every resource it creates a clear owner.

In DeepSeek Harness, tool registrations, event listeners, model adapters, and the agent loop all enter this lifecycle through plugins. Unload is a runtime action, not a cleanup promise in documentation.

Where to go next

  • Composition and HMR
  • Events: Notify Without Knowing Who Listens
  • Cordis Inside DeepSeek Harness

Sources: lifecycle tutorial, Fiber implementation, and Context API.

All Posts

Author

avatar for DSH Research
DSH Research

Categories

  • Agent Infrastructure
01 / Fiber is the runtime handle for a plugin instance02 / Put external resources inside an effect03 / Common registrations are effects too04 / A small verifiable example05 / Effects are HMR infrastructureWhere to go next

More Posts

Cordis: Five Concepts for Reading a Plugin Tree
Agent Infrastructure

Cordis: Five Concepts for Reading a Plugin Tree

Build the smallest useful mental model of Cordis with Plugin, Context, inject, Events, and Effect.

avatar for DSH Research
DSH Research
2026/08/19
Cordis Inside DeepSeek Harness: Making Everything a Plugin
Agent Infrastructure

Cordis Inside DeepSeek Harness: Making Everything a Plugin

Trace dsh-base, Context services, event domains, and the tool pipeline to see how Cordis organizes a replaceable Agent runtime.

avatar for DSH Research
DSH Research
2026/08/29
Services and Context: Share Capabilities Without Binding Implementations
Agent Infrastructure

Services and Context: Share Capabilities Without Binding Implementations

Use Service, Context, and declaration merging to understand how providers, consumers, and scopes connect DeepSeek Harness capabilities.

avatar for DSH Research
DSH Research
2026/08/22

Newsletter

Join the community

Subscribe to our newsletter for the latest news and updates

[dshub] [zsh]$ dshub ls$ dshub submit$ dshub blog
-- NORMAL -- ✓ 2026 dshub