A visual operating guide for coding agents

Cordis,
made
operational.

A composability runtime for systems that must keep changing without leaving behind stale dependencies, listeners, tools, timers, or assumptions.

Derived from cordis_system_guide_for_coding_agents.md
Target: the pinned DeepSeek Harness checkout and its vendored @deepseek-ai/cordis.
01 / 08
01 — MENTAL MODEL

A product is a plugin tree, not a pile of globals.

Cordis supplies composition semantics. DeepSeek Harness supplies the agent meanings: sessions, tools, LLMs, policies, shell, sandbox, and user surfaces.

Domain layer

DeepSeek Harness

Agent semantics are capabilities, not framework primitives.

  • agents + sessions
  • tools + prompt assembly
  • LLM + shell + filesystem
  • policies + UI + jobs
Composition layer

Cordis

Owns lifecycle, dependency topology, scopes, and reversible contribution.

  • plugins + Fibers
  • services + inject
  • effects + disposers
  • Loader + HMR
Execution layer

Node / OS

The external world that plugins can safely integrate with, but cannot magically roll back.

  • processes + sockets
  • files + timers
  • network + persistence
  • external services
01Plugin

The independently mountable lifecycle boundary.

02Fiber

One live activation instance and its cleanup ownership.

03Context

The scoped capability environment a plugin can see and alter.

04Service

A stable, named capability - never a concrete provider import.

02 — LIFECYCLE

Two dimensions of safe change.

Spatiotemporal composability combines clean withdrawal with live dependency rebinding. Either alone is insufficient.

Temporal composability
Leave cleanly.

When Plugin A unloads, its tool, listener, timer, watcher, and provider disappear. Plugin B’s independent contributions remain.

acquire
ctx.effect(() => {
  const timer = setInterval(work, 1000)
  return () => clearInterval(timer)
})
release
Spatial composability
Rebind safely.

When the current llm, shell, or memory provider changes identity, consumers unload their active episode and reactivate against the replacement.

BEFOREProvider A

Consumer is active.

CHANGEUnload

Owned contributions withdraw.

AFTERProvider B

Consumer reactivates.

One ownership rule
Acquire and release in the same activation scope. If Cordis does not already own a resource, acquire it inside ctx.effect() and return the disposer. Keep order-dependent teardown inside one disposer.
Fiber activation states
PENDING

Mounted, but required capability missing.

LOADING

Activation and setup in progress.

ACTIVE

Current valid activation episode.

UNLOADING

Effects and children settle.

DISPOSED

Runtime identity is permanently gone.

03 — COMPOSITION

Make dependency topology explicit.

Configuration says what exists. Injection says what must be ready. YAML row order is never the dependency system.

The capability seam
CONTRACTDefinition

Interface and request/result types.

IMPLEMENTProvider

Service implementation.

USEConsumer

Tool, feature, or workflow.

Use this three-role seam when implementations must vary independently. Do not create three packages for a tiny, one-off helper.

Required vs optional
Hard requirementinject = ['tools']

Plugin must not run without the capability.

Optional enhancementctx.get('metrics')

Plugin is valid and complete without it.

Scoped realmctx.isolate('shell')

Different subtrees resolve the same service name independently.

Scoped configctx.intercept(...)

Use only when the service explicitly supports interception.

The core plugin contract
export const name = 'greet-tool'
export const inject = ['tools', 'greeter']

export function apply(ctx: Context) {
  // registrations inherit this Fiber's lifecycle
  ctx.tools.register(defineTool({ /* ... */ }))

  // unmanaged resources must declare cleanup
  ctx.effect(() => {
    const watcher = watch(path, onChange)
    return () => watcher.close()
  })
}
04 — HARNESS

Cordis is the chassis. The Harness is the agent runtime.

A model-facing tool is not a Cordis primitive. It is an ordinary plugin registering into the Harness tools Service.

One accepted input becomes a Turn
turn/start

Claim input. Assemble system prompt and available tool schemas.

step/start

Persist input, derive model history, and dispatch agent/request.

llm/stream

Receive assistant chunks and message. Run any emitted tool calls.

tool pipeline

tools/pre-executetools/executetools/post-executetool/result.

step/end

Repeat only if work remains. Otherwise emit turn completion.

TransientEvents

Runtime observation and policy seams: emit, parallel, serial, waterfall.

DurableSession log

Replay-critical model-visible facts: messages, tool calls, outcomes, turn history.

Critical distinctionNot the same

A durable session record does not automatically create a same-named live Cordis event.

Waterfall listeners must call next() unless they deliberately veto or replace downstream behavior. Forgetting it silently swallows the core operation.
05 — DECISIONS

Choose the smallest correct Cordis primitive.

A feature may involve several primitives, but each has a distinct job. Do not let one concept impersonate another.

NeedUseWhy
Mountable or replaceable behaviorpluginLifecycle and composition boundary.
Direct callable capabilityServiceStable provider abstraction for code.
Hard dependencyinjectActivation gates on a live service implementation.
Raw resource with cleanupctx.effect()Explicit Fiber-owned disposer.
Open notification or policy seameventProducer does not know observers.
Model-callable operationctx.tools.register()Harness tool registry, not framework magic.
Per-subtree providerctx.isolate()Service realm, not an OS security boundary.
Durable replay/model factsession event/logDo not keep it only in a transient callback.
Configuration + HMR

Use stable row IDs in cordis.yml. Treat apply() as something that can happen repeatedly in one process.

- id: llm-provider
  name: './providers/deepseek.ts'

- id: my-tools
  name: './tools/index.ts'
Transactional reconciliation

The Harness Loader tries to validate a changed candidate before destroying a working entry, then restores a prior config on failure when possible. It cannot rescue unmanaged global timers, stale module singletons, or irreversible external mutations.

06 — DEBUG

Diagnose the graph before adding retries.

Most “nothing happened” and “it fired twice” failures are lifecycle or dependency-graph mistakes, not timing problems.

When a plugin prints nothing
  1. Confirm the row exists in effective configuration.
  2. Confirm module resolution and disabled state.
  3. Inspect the Fiber state.
  4. If PENDING, locate the missing injected service.
  5. Inspect the provider’s own state and config validation.
  6. Check whether HMR rejected and rolled back the candidate.
When HMR duplicates behavior
  1. Inventory every process contribution made by apply().
  2. Use lifecycle-aware registries for listeners and tools.
  3. Wrap unmanaged watchers, sockets, timers, and emitters in ctx.effect().
  4. Put dependent teardown in one disposer where ordering matters.
  5. Reload repeatedly and verify exactly one live registration remains.
The four questions every dynamic contribution must answer

Who owns me? What do I require? How do I leave? What happens when my provider changes?

07 — RULES

The coding-agent discipline.

Read local vendored code and generated Harness surfaces before assuming upstream examples apply. The local Harness checkout is the implementation authority.

Must
  • Declare every hard service dependency.
  • Make every activation-owned resource disposable.
  • Assume repeat activation in one process.
  • Validate config before partial activation.
  • Test unload and reload, not startup only.
Should
  • Use function plugins by default.
  • Use Service classes for public capabilities.
  • Keep stable Loader row IDs.
  • Keep provider specifics behind contracts.
  • Use durable history for replay-critical facts.
Must not
  • Encode dependency order in YAML.
  • Import concrete providers in consumers.
  • Start detached promises with unmanaged resources.
  • Treat isolation as a security sandbox.
  • Claim effect cleanup undoes external history.
08 — REVIEW

Before writing code, write the mini-design.

This review template turns the framework’s invariants into a repeatable agent workflow.

PromptWhat to write before implementation
CapabilityThe problem, external contract, and owning domain subsystem.
Plugin boundaryWhy it needs independent lifecycle, replacement, or config.
Provides / injectsStable service names, required dependencies, and optional dependencies.
Effects / disposalEvery listener, timer, watcher, process, socket, and cleanup order.
Config / LoaderSchema, safe defaults, stable IDs, and update behavior.
Events / durabilityLive event mode and which facts must become session records.
HMR / replacementWhy repeated activation and provider replacement remain safe.
TestsStartup, unload, reload, provider replacement, error, and cancellation paths.
The one-paragraph memory

Make behavior a plugin only when it benefits from independent lifecycle or composition. Declare required capabilities with inject; access them through stable service names. Treat every registration and resource as a Fiber-owned effect. Design for activation, cleanup, and activation again. Persist model-visible facts durably. Never confuse a flexible plugin graph with a security sandbox.