# How the Cascade Engine Handles Service Updates in agent-core-v2

> Discover how the cascade engine in agent-core-v2 manages service updates. Learn about pending services, dependency teardown, and reactivation for seamless updates.

- Repository: [Moonshot AI/kimi-code](https://github.com/MoonshotAI/kimi-code)
- Tags: internals
- Published: 2026-08-13

---

**The cascade engine processes service updates as deterministic, tree-wide transactions that mark services as pending, tear down affected dependents, and reactivate them in topological order once dependencies are satisfied.**

The cascade engine serves as the core dependency injection (DI) system in MoonshotAI/kimi-code's agent-core-v2. When you trigger a service update, the engine orchestrates an atomic, scope-wide re-evaluation that guarantees consistency across the entire service tree without immediately recreating instances.

## Submitting an Update Request

Service updates begin by calling `engine.update(token, reason)` in [`packages/agent-core-v2/src/_base/di/cascadeEngine.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/_base/di/cascadeEngine.ts). This method creates a `CascadeChange` object with `action: 'update'` and queues it internally via the `submit()` method (lines 38-45).

The update request does not execute immediately. Instead, it enters a batch queue where the engine aggregates multiple changes before processing them as a single transaction. This batching strategy prevents partial states and ensures that cascading effects are computed holistically.

## Transaction Processing and the Contagion Set

Once queued, the request is pulled by the private `_pump()` method and passed to `_transact()`. Here, the engine merges the batch and computes the **contagion set**—the complete set of service tokens potentially affected by the change (lines 71-76).

The engine determines this set using the global `DependencyGraph` defined in [`packages/agent-core-v2/src/_base/di/dependencyGraph.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/_base/di/dependencyGraph.ts). By analyzing dependency relationships, the engine identifies not just the updated service, but all dependents that might become invalid due to the change.

Before proceeding with teardown, the engine checks for an optional `onWillCascade` hook. If provided, the transaction waits up to `abortWaitMs` milliseconds for the hook to complete, allowing external systems to prepare for the incoming changes (lines 48-57).

## Teardown and Pending State Management

For every token in the contagion set, the cascade engine calls `_teardownForCascade()`. Critically, because an **update** action does not *unprovide* the token, the service is **parked as pending** rather than fully destroyed (lines 24-34).

This distinction is fundamental to the update semantics: the service instance remains in a `Pending` state while the engine evaluates whether its dependencies (or dependents) have changed. The `_applyChangeForCascade()` method handles this transition by explicitly marking the token as `Pending` or maintaining its pending status (lines 64-66). No new instance is created at this stage.

## Reactivation and Topological Ordering

After teardown completes, `_recheckPending()` repeatedly scans the pending index to determine which services can be safely reactivated. The engine builds a **topological order** of services whose dependencies are now satisfied and activates them via `_activate()` (lines 70-77, 87-96).

During this phase:

- Satisfied dependencies trigger `host.materialize`, moving the service to the `Active` state
- Failures during activation are captured, resulting in a `Failed` state for that service
- The process respects the dependency graph ordering to ensure that upstream services activate before their dependents

## Transaction Completion and History Logging

Once the transaction finishes, the engine pushes a `CascadeHistoryEntry` to its internal history buffer. This entry captures the reason for the cascade, the affected tokens, and the final states of torn-down, rebuilt, and failed services (lines 133-141).

You can observe state changes in real time by subscribing to `engine.onDidChangeUnitState()`, which emits events containing the token and its new state (`Pending`, `Active`, or `Failed`).

## Practical Implementation

```typescript
import { CascadeEngine, CascadeHost, CascadeScopeHandle } from '#/packages/agent-core-v2/src/_base/di';

// Assume we have a host implementation that knows about `MyService`
const host: CascadeHost = …;
const scopeHandle: CascadeScopeHandle = …;
const tree = new CascadeTree(/* DependencyGraph instance */);
const engine = new CascadeEngine(host, scopeHandle, tree);

// Trigger an update of a registered service
await engine.update(MyService, 'Refresh configuration after user change');

```

The call above enqueues an update, runs the full transaction described above, and returns only when the service and its dependents have settled into their final states.

To monitor state transitions during updates:

```typescript
// Listening for state changes (e.g., to update UI)
engine.onDidChangeUnitState(event => {
  console.log(`Service ${event.token} is now ${event.state}`);
});

```

## Key Source Files

- [`packages/agent-core-v2/src/_base/di/cascadeEngine.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/_base/di/cascadeEngine.ts) — Core implementation of submission, transaction processing, teardown, and activation
- [`packages/agent-core-v2/src/_base/di/dependencyGraph.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/_base/di/dependencyGraph.ts) — Provides graph analysis for contagion sets and topological sorting
- [`packages/agent-core-v2/src/_base/di/errors.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/_base/di/errors.ts) — Defines `CascadeConflictError` and other error types encountered during updates
- [`packages/agent-core-v2/test/_base/di/cascade.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/test/_base/di/cascade.test.ts) — Test suite verifying update scenarios and cascade behavior

## Summary

- Update requests are queued as `CascadeChange` objects with `action: 'update'` and processed via `engine.update()`
- The engine computes a **contagion set** using the `DependencyGraph` to identify all potentially affected services across the scope tree
- Updated services are **marked as Pending** rather than immediately recreated, preserving instance lifecycle boundaries
- Dependent services are torn down and rebuilt in **topological order** once dependencies are satisfied via `_recheckPending()` and `_activate()`
- Complete transaction history is preserved via `CascadeHistoryEntry` for debugging and audit purposes

## Frequently Asked Questions

### What is the contagion set in agent-core-v2 service updates?

The contagion set is the complete collection of service tokens that might be affected by an update, computed by the `_transact()` method using the global `DependencyGraph`. It includes the directly updated service and all transitive dependents, ensuring the cascade engine evaluates the full impact tree before making any state changes.

### Why doesn't the cascade engine immediately recreate service instances during an update?

The engine treats updates as **dependency re-evaluations** rather than destruction-recreation cycles. By marking services as `Pending` via `_applyChangeForCascade()`, the engine can first tear down dependents that might become invalid, then selectively rebuild only those services whose dependencies are actually satisfied, preventing unnecessary instantiation and maintaining atomic consistency.

### How does the cascade engine handle failures during service reactivation?

During the `_recheckPending()` phase, if `_activate()` encounters an error while materializing a service via `host.materialize`, the engine captures the exception and transitions the service to the `Failed` state. This failure is recorded in the `CascadeHistoryEntry`, allowing the transaction to complete while preserving error information for subsequent handling.

### What is the purpose of the onWillCascade hook?

The optional `onWillCascade` hook provides a grace period before teardown begins. When provided, the engine waits up to `abortWaitMs` milliseconds for this hook to resolve, allowing external systems (such as UI components or network clients) to prepare for service destruction. If the hook fails or times out, the transaction proceeds normally, but the delay allows for coordinated shutdown sequences.