# Understanding the Adaptive Flush Mechanism in OfficeCLI Resident Mode

> Discover OfficeCLI Resident Mode's adaptive flush mechanism. This smart feature optimizes autosave intervals using EMA, ensuring background serialization uses under 25% of your time.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: internals
- Published: 2026-07-14

---

**The adaptive flush mechanism in OfficeCLI Resident Mode automatically adjusts autosave intervals between 2 and 10 seconds based on the exponential moving average of previous save durations, ensuring background serialization never consumes more than 25% of wall-clock time.**

OfficeCLI's Resident Mode keeps documents in memory for rapid command execution, deferring disk writes until specific flush points. The adaptive flush mechanism, implemented as the default `ResidentFlushMode.Auto` strategy in the `iOfficeAI/OfficeCLI` repository, dynamically tunes these intervals based on measured save costs to balance data safety with performance.

## How the Adaptive Flush Interval Is Computed

The algorithm, defined in [`src/officecli/Core/ResidentFlushPolicy.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/ResidentFlushPolicy.cs), calculates the debounce interval through a five-stage process that responds to serialization latency in real time.

### Cost Model and Bounds

The system constrains CPU usage using a cost multiplier and hard interval limits. `ResidentFlushPolicy.CostMultiplier` is set to `4.0`, ensuring autosave operations never exceed approximately 25% of available wall-clock time. Two constants define the acceptable range: `MinAdaptiveInterval` at `2` seconds and `MaxAdaptiveInterval` at `10` seconds.

### Exponential Moving Average (EMA) Calculation

Instead of using raw save durations, the mechanism employs an asymmetric EMA that reacts differently to speed changes. The `ResidentFlushPolicy.NextEmaSeconds(previous, sample)` method applies `RiseAlpha = 0.7` when saves slow down, causing the EMA to spike immediately on expensive operations. When saves speed up, it uses `FallAlpha = 0.2`, allowing the average to decrease gradually and preventing "save storms" from rapid successive triggers.

### Interval Derivation and Updating

The `ResidentFlushPolicy.IntervalForEma(ema)` method derives the next interval by multiplying the current EMA by the cost multiplier (clamped to the 2-10 second bounds). After each successful save, `ResidentServer.RecordSaveDuration(elapsed)` updates `_saveEmaMillis` and `_adaptiveIntervalTicks`, instantly influencing the next watchdog timeout.

## Implementation in the Autosave Watchdog

The adaptive value drives the background persistence loop in [`src/officecli/ResidentServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/ResidentServer.cs). Inside `RunAutosaveWatchdogAsync`, the watchdog queries `CurrentAutosaveInterval` before each delay:

```csharp
await Task.Delay(CurrentAutosaveInterval, linked.Token);
await TryAutosaveAsync(token);

```

The `CurrentAutosaveInterval` property resolves to either a fixed user-provided duration or the adaptive value stored in `_adaptiveIntervalTicks`:

```csharp
private TimeSpan CurrentAutosaveInterval => FlushMode == ResidentFlushMode.Fixed
    ? FixedFlushInterval
    : TimeSpan.FromTicks(Volatile.Read(ref _adaptiveIntervalTicks));

```

This design ensures that the adaptive calculation directly controls the `Task.Delay` duration without restarting the watchdog process.

## Why Adaptive Flush Improves Performance

The mechanism provides three key benefits for document-heavy workflows:

- **Back-off on heavy saves**: When serializing large Excel workbooks takes seconds, the EMA immediately rises toward the 10-second ceiling, preventing expensive operations from dominating CPU cycles.
- **Quick recovery on fast saves**: Rapid saves only nudge the EMA down slowly via the 0.2 fall alpha, maintaining stability rather than collapsing the interval prematurely.
- **Bounded CPU use**: The 4.0 cost multiplier guarantees that autosave never consumes more than ~25% of wall-clock time, regardless of underlying serialization costs.

## Configuring Resident Mode Flush Behavior

Developers can control persistence strategy through environment variables before invoking `officecli open`:

| Variable | Purpose | Example |
|----------|---------|---------|
| `OFFICECLI_RESIDENT_FLUSH` | Sets flush mode (`auto`, `each`, fixed seconds, or `off`) | `export OFFICECLI_RESIDENT_FLUSH=auto` |
| `OFFICECLI_RESIDENT_IDLE_SECONDS` | Idle timeout before shutdown (unrelated to autosave) | `export OFFICECLI_RESIDENT_IDLE_SECONDS=720` |

When `OFFICECLI_RESIDENT_FLUSH` is unset or set to `auto`, the server defaults to the adaptive algorithm described above.

To enable adaptive autosave explicitly:

```bash
export OFFICECLI_RESIDENT_FLUSH=auto
export OFFICECLI_RESIDENT_IDLE_SECONDS=720

officecli open large-workbook.xlsx

```

## Summary

- The **adaptive flush mechanism** in OfficeCLI Resident Mode uses an exponential moving average (EMA) of save durations to calculate autosave intervals dynamically.
- Intervals are constrained between **2 and 10 seconds** and derived using a **4.0 cost multiplier** to limit CPU usage to approximately 25%.
- The algorithm applies **asymmetric smoothing** (`RiseAlpha = 0.7`, `FallAlpha = 0.2`) to respond immediately to slow saves while recovering gradually from fast ones.
- Core logic resides in [`src/officecli/Core/ResidentFlushPolicy.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/ResidentFlushPolicy.cs), while [`src/officecli/ResidentServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/ResidentServer.cs) implements the watchdog loop that consumes the adaptive interval via `CurrentAutosaveInterval`.

## Frequently Asked Questions

### How does OfficeCLI determine when to autosave in Resident Mode?

The resident server calculates the next autosave delay using the `ResidentFlushPolicy` class. It measures the wall-clock time of each save operation, feeds that duration into an EMA calculation with asymmetric alpha values (0.7 for rises, 0.2 for falls), and multiplies the result by 4.0 to determine the interval. This value is then clamped between 2 and 10 seconds before being passed to the watchdog's `Task.Delay`.

### Why does the adaptive flush use different alpha values for rising and falling EMA?

The asymmetric alphas prevent oscillation between fast and slow save states. When a save suddenly becomes expensive (e.g., due to document growth), `RiseAlpha = 0.7` immediately raises the EMA toward the new reality, backing off quickly to conserve CPU. Conversely, `FallAlpha = 0.2` ensures that when saves become cheap again, the interval descends slowly, preventing a "save storm" where too frequent disk writes hurt performance.

### Can I set a fixed autosave interval instead of using the adaptive mechanism?

Yes. Set the environment variable `OFFICECLI_RESIDENT_FLUSH` to a positive integer representing seconds (e.g., `export OFFICECLI_RESIDENT_FLUSH=5`) or use `each` to flush after every command. When `FlushMode` is set to `Fixed`, the `CurrentAutosaveInterval` property bypasses the adaptive calculation and uses the user-provided `FixedFlushInterval` instead.

### What happens if a save operation takes longer than the current adaptive interval?

The EMA calculation immediately incorporates the long duration via the high rise alpha (0.7), causing subsequent intervals to stretch toward the 10-second maximum. Because the cost multiplier caps background work at roughly 25% of wall-clock time, even pathologically slow saves cannot dominate the process indefinitely—the interval will simply max out at 10 seconds until performance improves.