// Coverage for deferred context-engine maintenance and transcript rewrite hooks.

import { expectDefined } from "@openclaw/normalization-core";
import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ContextEngineRuntimeContext } from "../../context-engine/types.js";
import { peekSystemEvents, resetSystemEventsForTest } from "../../infra/system-events.js";
import { enqueueCommandInLane, markGatewayDraining } from "../../process/command-queue.js";
import * as commandQueueModule from "../../process/command-queue.js";
import { resetCommandQueueStateForTest } from "../../process/command-queue.test-support.js";
import { onSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
import { createQueuedTaskRun as createQueuedTaskRunOrNull } from "../../tasks/task-executor.js";
import { getTaskFlowById } from "../../tasks/task-flow-registry.js";
import { getTaskById, listTasksForOwnerKey } from "../../tasks/task-registry.js";
import type { TaskRecord } from "../../tasks/task-registry.types.js";
import {
  resetTaskFlowRegistryForTests,
  resetTaskRegistryForTests,
  setTaskRegistryDeliveryRuntimeForTests,
} from "../../tasks/task-runtime.test-helpers.js";
import { withStateDirEnv } from "../../test-helpers/state-dir-env.js";
import { castAgentMessage } from "../test-helpers/agent-message-fixtures.js";
import { resolveSessionLane } from "./lanes.js";

const rewriteTranscriptEntriesInSessionManagerMock = vi.fn((_params?: unknown) => ({
  changed: true,
  bytesFreed: 77,
  rewrittenEntries: 1,
}));
const openedSessionManager = { kind: "opened-session-manager" };
const sessionManagerOpenMock = vi.fn((_target?: unknown) => openedSessionManager);
const resolveRuntimeTranscriptReadTargetMock = vi.fn(async (scope: Record<string, unknown>) => ({
  agentId: scope.agentId ?? "main",
  sessionId: scope.sessionId,
  sessionKey: scope.sessionKey,
  storePath: scope.storePath ?? "/tmp/default-openclaw.sqlite",
}));
let createDeferredTurnMaintenanceAbortSignal: typeof import("./context-engine-maintenance.test-support.js").createDeferredTurnMaintenanceAbortSignal;
let resetDeferredTurnMaintenanceStateForTest: typeof import("./context-engine-maintenance.test-support.js").resetDeferredTurnMaintenanceStateForTest;
let waitForDeferredTurnMaintenanceForSession: typeof import("./context-engine-maintenance.js").waitForDeferredTurnMaintenanceForSession;

function createQueuedTaskRun(params: Parameters<typeof createQueuedTaskRunOrNull>[0]): TaskRecord {
  // Task creation can legally return null for invalid inputs; tests here always
  // need a concrete queued task record.
  const task = createQueuedTaskRunOrNull(params);
  if (!task) {
    throw new Error("expected queued task creation to succeed");
  }
  return task;
}
let runContextEngineMaintenance: typeof import("./context-engine-maintenance.js").runContextEngineMaintenance;
// Keep this literal aligned with the production module; tests use dynamic
// import reloading, so they cannot safely import the constant directly.
const TURN_MAINTENANCE_TASK_KIND = "context_engine_turn_maintenance";

async function flushAsyncWork(times = 4): Promise<void> {
  for (let index = 0; index < times; index += 1) {
    await Promise.resolve();
  }
}

async function waitForAssertion(
  assertion: () => void,
  timeoutMs = 2_000,
  stepMs = 5,
): Promise<void> {
  // Timed polling lets fake-timer tasks advance through queue and delivery
  // microtasks without binding assertions to a specific internal await count.
  const startedAt = Date.now();
  for (;;) {
    try {
      assertion();
      return;
    } catch (error) {
      if (Date.now() - startedAt >= timeoutMs) {
        throw error;
      }
      await vi.advanceTimersByTimeAsync(stepMs);
      await flushAsyncWork();
    }
  }
}

const requireRecord = createRequireRecord("record", "expected-label");

function firstMaintainParams(maintain: { mock: { calls: unknown[][] } }): Record<string, unknown> {
  return requireRecord(maintain.mock.calls[0]?.[0], "maintain params");
}

function expectRecordFields(record: Record<string, unknown>, expected: Record<string, unknown>) {
  for (const [key, value] of Object.entries(expected)) {
    expect(record[key]).toBe(value);
  }
}

function expectSystemEventContaining(sessionKey: string, text: string) {
  expect(peekSystemEvents(sessionKey).join("\n")).toContain(text);
}

vi.mock("./context-engine-capabilities.js", () => ({
  resolveContextEngineCapabilities: () => ({ llm: undefined }),
}));

vi.mock("./transcript-rewrite.js", () => ({
  rewriteTranscriptEntriesInSessionManager: (params: unknown) =>
    rewriteTranscriptEntriesInSessionManagerMock(params),
}));

vi.mock("../sessions/index.js", () => ({
  SessionManager: { open: (target: unknown) => sessionManagerOpenMock(target) },
}));

vi.mock("./transcript-runtime-state.js", () => ({
  resolveRuntimeTranscriptReadTarget: (scope: Record<string, unknown>) =>
    resolveRuntimeTranscriptReadTargetMock(scope),
}));

async function loadFreshContextEngineMaintenanceModuleForTest() {
  // The module owns singleton deferred-maintenance state, so reload between
  // cases before asserting abort or queue behavior.
  ({ runContextEngineMaintenance, waitForDeferredTurnMaintenanceForSession } =
    await import("./context-engine-maintenance.js"));
  ({ createDeferredTurnMaintenanceAbortSignal, resetDeferredTurnMaintenanceStateForTest } =
    await import("./context-engine-maintenance.test-support.js"));
  resetDeferredTurnMaintenanceStateForTest();
}

describe("createDeferredTurnMaintenanceAbortSignal", () => {
  beforeEach(async () => {
    await loadFreshContextEngineMaintenanceModuleForTest();
  });

  it("aborts on termination signals and unregisters listeners", () => {
    const listeners = new Map<string, Set<() => void>>();
    const kill = vi.fn();
    const processLike = {
      on(event: "SIGINT" | "SIGTERM", listener: () => void) {
        const bucket = listeners.get(event) ?? new Set<() => void>();
        bucket.add(listener);
        listeners.set(event, bucket);
        return this;
      },
      off(event: "SIGINT" | "SIGTERM", listener: () => void) {
        listeners.get(event)?.delete(listener);
        return this;
      },
      listenerCount(event: "SIGINT" | "SIGTERM") {
        return listeners.get(event)?.size ?? 0;
      },
      kill,
      pid: 4242,
    } as unknown as NonNullable<
      Parameters<typeof createDeferredTurnMaintenanceAbortSignal>[0]
    >["processLike"];

    const { abortSignal, dispose } = createDeferredTurnMaintenanceAbortSignal({ processLike });
    const second = createDeferredTurnMaintenanceAbortSignal({ processLike });
    expect(listeners.get("SIGINT")?.size ?? 0).toBe(1);
    expect(listeners.get("SIGTERM")?.size ?? 0).toBe(1);

    const sigtermListeners = Array.from(listeners.get("SIGTERM") ?? []);
    expect(sigtermListeners).toHaveLength(1);
    sigtermListeners[0]?.();

    expect(abortSignal?.aborted).toBe(true);
    expect(second.abortSignal?.aborted).toBe(true);
    expect(kill).toHaveBeenCalledWith(4242, "SIGTERM");
    expect(listeners.get("SIGINT")?.size ?? 0).toBe(0);
    expect(listeners.get("SIGTERM")?.size ?? 0).toBe(0);

    dispose();
    second.dispose();
    expect(listeners.get("SIGINT")?.size ?? 0).toBe(0);
    expect(listeners.get("SIGTERM")?.size ?? 0).toBe(0);
  });
});

describe("runContextEngineMaintenance", () => {
  beforeEach(async () => {
    vi.useRealTimers();
    rewriteTranscriptEntriesInSessionManagerMock.mockClear();
    sessionManagerOpenMock.mockClear();
    resolveRuntimeTranscriptReadTargetMock.mockClear();
    await loadFreshContextEngineMaintenanceModuleForTest();
  });

  it("passes a rewrite-capable runtime context into maintain()", async () => {
    const sessionTarget = {
      agentId: "main",
      sessionId: "session-1",
      sessionKey: "agent:main:session-1",
      storePath: "/tmp/state/openclaw.sqlite",
    };
    const maintain = vi.fn(async (_params?: unknown) => ({
      changed: false,
      bytesFreed: 0,
      rewrittenEntries: 0,
    }));

    const result = await runContextEngineMaintenance({
      contextEngine: {
        info: { id: "test", name: "Test Engine" },
        ingest: async () => ({ ingested: true }),
        assemble: async ({ messages }) => ({ messages, estimatedTokens: 0 }),
        compact: async () => ({ ok: true, compacted: false }),
        maintain,
      },
      sessionId: "session-1",
      sessionKey: "agent:main:session-1",
      sessionTarget,
      sessionFile: "/tmp/session.jsonl",
      reason: "turn",
      runtimeContext: { workspaceDir: "/tmp/workspace" },
    });

    expect(result).toEqual({
      changed: false,
      bytesFreed: 0,
      rewrittenEntries: 0,
    });
    const maintainParams = firstMaintainParams(maintain);
    expectRecordFields(maintainParams, {
      sessionId: "session-1",
      sessionKey: "agent:main:session-1",
      sessionTarget,
      sessionFile: "/tmp/session.jsonl",
    });
    const maintainRuntimeContext = requireRecord(
      maintainParams.runtimeContext,
      "maintain runtime context",
    );
    expect(maintainRuntimeContext.workspaceDir).toBe("/tmp/workspace");
    expect(maintainRuntimeContext.sessionTarget).toEqual(sessionTarget);
    const runtimeContext = maintainParams.runtimeContext as
      | { rewriteTranscriptEntries?: (request: unknown) => Promise<unknown> }
      | undefined;
    if (!runtimeContext?.rewriteTranscriptEntries) {
      throw new Error("expected maintain runtime context rewrite helper");
    }
    const rewriteResult = await runtimeContext.rewriteTranscriptEntries({
      replacements: [
        { entryId: "entry-2", message: { role: "user", content: "hello", timestamp: 2 } },
      ],
    });
    expect(rewriteResult).toEqual({
      changed: true,
      bytesFreed: 77,
      rewrittenEntries: 1,
    });
    expect(sessionManagerOpenMock).toHaveBeenCalledWith(sessionTarget);
    expect(rewriteTranscriptEntriesInSessionManagerMock).toHaveBeenCalledWith({
      sessionManager: openedSessionManager,
      replacements: [
        { entryId: "entry-2", message: { role: "user", content: "hello", timestamp: 2 } },
      ],
    });
  });

  it("forces background maintenance rewrites through the runtime target even when a session manager exists", async () => {
    const maintain = vi.fn(async (params?: unknown) => {
      await (
        params as { runtimeContext?: ContextEngineRuntimeContext } | undefined
      )?.runtimeContext?.rewriteTranscriptEntries?.({
        replacements: [
          {
            entryId: "entry-1",
            message: castAgentMessage({
              role: "assistant",
              content: [{ type: "text", text: "done" }],
              timestamp: 2,
            }),
          },
        ],
      });
      return {
        changed: false,
        bytesFreed: 0,
        rewrittenEntries: 0,
      };
    });
    const sessionManager = { appendMessage: vi.fn() } as unknown as Parameters<
      typeof runContextEngineMaintenance
    >[0]["sessionManager"];
    const transcriptUpdateListener = vi.fn();
    const cleanupTranscriptUpdateListener = onSessionTranscriptUpdate(transcriptUpdateListener);

    try {
      await runContextEngineMaintenance({
        contextEngine: {
          info: { id: "test", name: "Test Engine", turnMaintenanceMode: "background" },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }) => ({ messages, estimatedTokens: 0 }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        },
        sessionId: "session-background-file-rewrite",
        sessionKey: "agent:main:session-background-file-rewrite",
        sessionTarget: {
          agentId: "custom-agent",
          sessionId: "custom-session",
          sessionKey: "agent:custom-agent:custom-session",
          storePath: "/tmp/custom-agent.sqlite",
        },
        sessionFile: "/tmp/session-background-file-rewrite.jsonl",
        reason: "turn",
        executionMode: "background",
        sessionManager,
        config: {},
      });
    } finally {
      cleanupTranscriptUpdateListener();
    }

    expect(resolveRuntimeTranscriptReadTargetMock).toHaveBeenCalledWith({
      agentId: "custom-agent",
      sessionId: "custom-session",
      sessionKey: "agent:custom-agent:custom-session",
      sessionFile: "/tmp/session-background-file-rewrite.jsonl",
      storePath: "/tmp/custom-agent.sqlite",
    });
    expect(sessionManagerOpenMock).toHaveBeenCalledWith({
      agentId: "custom-agent",
      sessionId: "custom-session",
      sessionKey: "agent:custom-agent:custom-session",
      storePath: "/tmp/custom-agent.sqlite",
    });
    expect(rewriteTranscriptEntriesInSessionManagerMock).toHaveBeenCalledWith({
      sessionManager: openedSessionManager,
      replacements: [
        {
          entryId: "entry-1",
          message: castAgentMessage({
            role: "assistant",
            content: [{ type: "text", text: "done" }],
            timestamp: 2,
          }),
        },
      ],
    });
    expect(transcriptUpdateListener).toHaveBeenCalledWith({
      agentId: "custom-agent",
      sessionId: "custom-session",
      sessionKey: "agent:custom-agent:custom-session",
      target: {
        agentId: "custom-agent",
        sessionId: "custom-session",
        sessionKey: "agent:custom-agent:custom-session",
      },
    });
  });

  it("locks foreground maintenance rewrites that use the active session manager", async () => {
    const events: string[] = [];
    const maintain = vi.fn(async (params?: unknown) => {
      events.push("maintain-start");
      await (
        params as { runtimeContext?: ContextEngineRuntimeContext } | undefined
      )?.runtimeContext?.rewriteTranscriptEntries?.({
        replacements: [
          { entryId: "entry-1", message: { role: "user", content: "hi", timestamp: 1 } },
        ],
      });
      events.push("maintain-end");
      return {
        changed: false,
        bytesFreed: 0,
        rewrittenEntries: 0,
      };
    });
    const sessionManager = { appendMessage: vi.fn() } as unknown as Parameters<
      typeof runContextEngineMaintenance
    >[0]["sessionManager"];
    rewriteTranscriptEntriesInSessionManagerMock.mockImplementationOnce((_params?: unknown) => {
      events.push("rewrite");
      return {
        changed: true,
        bytesFreed: 77,
        rewrittenEntries: 1,
      };
    });

    await runContextEngineMaintenance({
      contextEngine: {
        info: { id: "test", name: "Test Engine" },
        ingest: async () => ({ ingested: true }),
        assemble: async ({ messages }) => ({ messages, estimatedTokens: 0 }),
        compact: async () => ({ ok: true, compacted: false }),
        maintain,
      },
      sessionId: "session-foreground-manager-rewrite",
      sessionKey: "agent:main:session-foreground-manager-rewrite",
      sessionFile: "/tmp/session-foreground-manager-rewrite.jsonl",
      reason: "turn",
      sessionManager,
      withSessionManagerRewriteLock: async (operation) => {
        events.push("lock-start");
        try {
          return await operation();
        } finally {
          events.push("lock-end");
        }
      },
    });

    expect(events).toEqual(["maintain-start", "lock-start", "rewrite", "lock-end", "maintain-end"]);
    expect(rewriteTranscriptEntriesInSessionManagerMock).toHaveBeenCalledWith({
      sessionManager,
      replacements: [
        { entryId: "entry-1", message: { role: "user", content: "hi", timestamp: 1 } },
      ],
    });
    expect(sessionManagerOpenMock).not.toHaveBeenCalled();
  });

  it("defers turn maintenance to a hidden background task when enabled", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-1";
        const sessionLane = resolveSessionLane(sessionKey);
        let releaseForeground: (() => void) | undefined;
        const foregroundTurn = enqueueCommandInLane(sessionLane, async () => {
          await new Promise<void>((resolve) => {
            releaseForeground = resolve;
          });
        });
        await Promise.resolve();

        const maintain = vi.fn(async (params?: unknown) => {
          await (
            params as { runtimeContext?: ContextEngineRuntimeContext } | undefined
          )?.runtimeContext?.rewriteTranscriptEntries?.({
            replacements: [
              {
                entryId: "entry-1",
                message: castAgentMessage({
                  role: "assistant",
                  content: [{ type: "text", text: "done" }],
                  timestamp: 2,
                }),
              },
            ],
          });
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });

        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        const result = await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-1",
          sessionKey,
          sessionFile: "/tmp/session.jsonl",
          reason: "turn",
          runtimeContext: {
            workspaceDir: "/tmp/workspace",
            tokenBudget: 2048,
            currentTokenCount: 1536,
          },
          config: {},
        });

        expect(result).toBeUndefined();
        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
        await waitForAssertion(() =>
          expect(rewriteTranscriptEntriesInSessionManagerMock).toHaveBeenCalledWith({
            sessionManager: openedSessionManager,
            replacements: [
              {
                entryId: "entry-1",
                message: castAgentMessage({
                  role: "assistant",
                  content: [{ type: "text", text: "done" }],
                  timestamp: 2,
                }),
              },
            ],
          }),
        );

        const queuedTasks = listTasksForOwnerKey(sessionKey).filter(
          (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        expect(queuedTasks).toHaveLength(1);
        const queuedTask = requireRecord(queuedTasks[0], "queued task");
        expectRecordFields(queuedTask, {
          runtime: "acp",
          scopeKind: "session",
          ownerKey: sessionKey,
          requesterSessionKey: sessionKey,
          taskKind: TURN_MAINTENANCE_TASK_KIND,
          notifyPolicy: "silent",
          deliveryStatus: "not_applicable",
        });

        if (!releaseForeground) {
          throw new Error("Expected foreground turn release callback to be initialized");
        }
        releaseForeground();
        const maintainParams = firstMaintainParams(maintain);
        expectRecordFields(maintainParams, {
          sessionId: "session-1",
          sessionKey,
          sessionFile: "/tmp/session.jsonl",
        });
        expectRecordFields(requireRecord(maintainParams.runtimeContext, "runtime context"), {
          workspaceDir: "/tmp/workspace",
          allowDeferredCompactionExecution: true,
          tokenBudget: 2048,
          currentTokenCount: 1536,
        });

        await waitForAssertion(() =>
          expect(
            getTaskById(expectDefined(queuedTasks[0], "queuedTasks[0] test invariant").taskId)
              ?.status,
          ).toBe("succeeded"),
        );
        const completedTask = getTaskById(
          expectDefined(queuedTasks[0], "queuedTasks[0] test invariant").taskId,
        );
        const completedTaskRecord = requireRecord(completedTask, "completed task");
        expect(completedTaskRecord.status).toBe("succeeded");
        expect(String(completedTaskRecord.progressSummary)).toContain(
          "Deferred maintenance completed",
        );

        await foregroundTurn;
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("coalesces repeated requests into one active run plus one follow-up run for the same session", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-2";
        let releaseMaintenance: (() => void) | undefined;
        let maintenanceCalls = 0;
        const maintain = vi.fn(async () => {
          maintenanceCalls += 1;
          if (maintenanceCalls === 1) {
            await new Promise<void>((resolve) => {
              releaseMaintenance = resolve;
            });
          }
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });

        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-2",
          sessionKey,
          sessionFile: "/tmp/session-2.jsonl",
          reason: "turn",
        });
        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-2",
          sessionKey,
          sessionFile: "/tmp/session-2.jsonl",
          reason: "turn",
        });

        const queuedTasks = listTasksForOwnerKey(sessionKey).filter(
          (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        expect(queuedTasks).toHaveLength(1);

        if (!releaseMaintenance) {
          throw new Error("Expected maintenance release callback to be initialized");
        }
        releaseMaintenance();
        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(2));
        await waitForAssertion(() =>
          expect(
            listTasksForOwnerKey(sessionKey)
              .filter((task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND)
              .map((task) => task.status),
          ).toEqual(["succeeded", "succeeded"]),
        );
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("queues a follow-up maintenance run when a new turn finishes during an active deferred run", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-rerun-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-rerun";
        let releaseFirstMaintenance: (() => void) | undefined;
        let releaseSecondMaintenance: (() => void) | undefined;
        let maintenanceCalls = 0;
        const maintain = vi.fn(async () => {
          maintenanceCalls += 1;
          if (maintenanceCalls === 1) {
            await new Promise<void>((resolve) => {
              releaseFirstMaintenance = resolve;
            });
          }
          if (maintenanceCalls === 2) {
            await new Promise<void>((resolve) => {
              releaseSecondMaintenance = resolve;
            });
          }
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });

        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;
        const deferredPromises: Promise<void>[] = [];

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-rerun",
          sessionKey,
          sessionFile: "/tmp/session-rerun.jsonl",
          reason: "turn",
          onDeferredMaintenance: (promise) => {
            deferredPromises.push(promise);
          },
        });

        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-rerun",
          sessionKey,
          sessionFile: "/tmp/session-rerun.jsonl",
          reason: "turn",
          onDeferredMaintenance: (promise) => {
            deferredPromises.push(promise);
          },
        });
        expect(deferredPromises).toHaveLength(2);
        let secondDeferredSettled = false;
        const secondDeferred = expectDefined(
          deferredPromises[1],
          "deferredPromises[1] test invariant",
        ).then(() => {
          secondDeferredSettled = true;
        });

        if (!releaseFirstMaintenance) {
          throw new Error("Expected first maintenance release callback to be initialized");
        }
        releaseFirstMaintenance();
        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(2));
        await Promise.resolve();
        expect(secondDeferredSettled).toBe(false);

        if (!releaseSecondMaintenance) {
          throw new Error("Expected second maintenance release callback to be initialized");
        }
        releaseSecondMaintenance();
        await secondDeferred;
        expect(secondDeferredSettled).toBe(true);

        const tasks = listTasksForOwnerKey(sessionKey).filter(
          (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        expect(tasks).toHaveLength(2);
        expect(tasks.every((task) => task.status === "succeeded")).toBe(true);
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("disposes owned deferred engines only after their maintenance run finishes", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-dispose-", async () => {
      resetCommandQueueStateForTest();
      resetTaskRegistryForTests({ persist: false });
      resetTaskFlowRegistryForTests({ persist: false });
      const waitForRealAssertion = async (assertion: () => void): Promise<void> => {
        const startedAt = Date.now();
        for (;;) {
          try {
            assertion();
            return;
          } catch (error) {
            if (Date.now() - startedAt >= 2_000) {
              throw error;
            }
            await new Promise<void>((resolve) => {
              setTimeout(resolve, 5);
            });
          }
        }
      };

      const sessionKey = "agent:main:session-owned-dispose";
      const events: string[] = [];
      let releaseFirstMaintenance: (() => void) | undefined;
      let releaseSecondMaintenance: (() => void) | undefined;

      const createBackgroundEngine = (id: "first" | "second") =>
        ({
          info: {
            id,
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain: vi.fn(async () => {
            events.push(`maintain:${id}`);
            await new Promise<void>((resolve) => {
              if (id === "first") {
                releaseFirstMaintenance = resolve;
              } else {
                releaseSecondMaintenance = resolve;
              }
            });
            return {
              changed: false,
              bytesFreed: 0,
              rewrittenEntries: 0,
            };
          }),
          dispose: vi.fn(async () => {
            events.push(`dispose:${id}`);
          }),
        }) as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

      const firstEngine = createBackgroundEngine("first");
      const secondEngine = createBackgroundEngine("second");
      const deferredPromises: Promise<void>[] = [];

      await runContextEngineMaintenance({
        contextEngine: firstEngine,
        sessionId: "session-owned-dispose",
        sessionKey,
        sessionFile: "/tmp/session-owned-dispose.jsonl",
        reason: "turn",
        disposeDeferredContextEngineAfterMaintenance: true,
        onDeferredMaintenance: (promise) => {
          deferredPromises.push(promise);
        },
      });

      await waitForRealAssertion(() => expect(events).toContain("maintain:first"));

      await runContextEngineMaintenance({
        contextEngine: secondEngine,
        sessionId: "session-owned-dispose",
        sessionKey,
        sessionFile: "/tmp/session-owned-dispose.jsonl",
        reason: "turn",
        disposeDeferredContextEngineAfterMaintenance: true,
        onDeferredMaintenance: (promise) => {
          deferredPromises.push(promise);
        },
      });

      if (!releaseFirstMaintenance) {
        throw new Error("Expected first maintenance release callback to be initialized");
      }
      releaseFirstMaintenance();
      await waitForRealAssertion(() => expect(events).toContain("maintain:second"));
      expect(secondEngine["dispose"]).not.toHaveBeenCalled();

      if (!releaseSecondMaintenance) {
        throw new Error("Expected second maintenance release callback to be initialized");
      }
      releaseSecondMaintenance();
      await deferredPromises[1];

      expect(firstEngine["dispose"]).toHaveBeenCalledTimes(1);
      expect(secondEngine["dispose"]).toHaveBeenCalledTimes(1);
      expect(events).toEqual([
        "maintain:first",
        "dispose:first",
        "maintain:second",
        "dispose:second",
      ]);
    });
  });

  it("reports deferred maintenance schedule failure while gateway is draining", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-draining-", async () => {
      resetCommandQueueStateForTest();
      resetTaskRegistryForTests({ persist: false });
      resetTaskFlowRegistryForTests({ persist: false });

      const sessionKey = "agent:main:session-draining";
      const maintain = vi.fn(async () => ({
        changed: false,
        bytesFreed: 0,
        rewrittenEntries: 0,
      }));
      const onDeferredMaintenance = vi.fn();
      const onDeferredMaintenanceFailure = vi.fn();
      const backgroundEngine = {
        info: {
          id: "test",
          name: "Test Engine",
          turnMaintenanceMode: "background" as const,
        },
        ingest: async () => ({ ingested: true }),
        assemble: async ({ messages }: { messages: unknown[] }) => ({
          messages,
          estimatedTokens: 0,
        }),
        compact: async () => ({ ok: true, compacted: false }),
        maintain,
      } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

      markGatewayDraining();
      const result = await runContextEngineMaintenance({
        contextEngine: backgroundEngine,
        sessionId: "session-draining",
        sessionKey,
        sessionFile: "/tmp/session-draining.jsonl",
        reason: "turn",
        onDeferredMaintenance,
        onDeferredMaintenanceFailure,
      });

      expect(result).toBeUndefined();
      expect(onDeferredMaintenance).not.toHaveBeenCalled();
      expect(onDeferredMaintenanceFailure).toHaveBeenCalledOnce();
      expect(onDeferredMaintenanceFailure.mock.calls[0]?.[0]).toHaveProperty(
        "name",
        "GatewayDrainingError",
      );
      expect(maintain).not.toHaveBeenCalled();
      const tasks = listTasksForOwnerKey(sessionKey).filter(
        (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
      );
      expect(tasks).toEqual([]);
    });
  });

  it("rejects coalesced deferred maintenance requests while gateway is draining", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-draining-coalesced-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-draining-coalesced";
        let releaseMaintenance: (() => void) | undefined;
        const maintain = vi.fn(async () => {
          await new Promise<void>((resolve) => {
            releaseMaintenance = resolve;
          });
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });
        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;
        const firstDeferred: Promise<void>[] = [];

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-draining-coalesced",
          sessionKey,
          sessionFile: "/tmp/session-draining-coalesced.jsonl",
          reason: "turn",
          onDeferredMaintenance: (promise) => {
            firstDeferred.push(promise);
          },
        });
        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));

        const onDeferredMaintenance = vi.fn();
        const onDeferredMaintenanceFailure = vi.fn();
        markGatewayDraining();
        const result = await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-draining-coalesced",
          sessionKey,
          sessionFile: "/tmp/session-draining-coalesced.jsonl",
          reason: "turn",
          onDeferredMaintenance,
          onDeferredMaintenanceFailure,
        });

        expect(result).toBeUndefined();
        expect(onDeferredMaintenance).not.toHaveBeenCalled();
        expect(onDeferredMaintenanceFailure).toHaveBeenCalledOnce();
        expect(onDeferredMaintenanceFailure.mock.calls[0]?.[0]).toHaveProperty(
          "name",
          "GatewayDrainingError",
        );
        expect(maintain).toHaveBeenCalledTimes(1);

        if (!releaseMaintenance) {
          throw new Error("Expected maintenance release callback to be initialized");
        }
        releaseMaintenance();
        await firstDeferred[0];
      } finally {
        resetCommandQueueStateForTest();
        vi.useRealTimers();
      }
    });
  });

  it("replaces legacy active maintenance tasks that are missing a runId", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-legacy";
        const legacyTask = createQueuedTaskRun({
          runtime: "acp",
          taskKind: TURN_MAINTENANCE_TASK_KIND,
          sourceId: TURN_MAINTENANCE_TASK_KIND,
          requesterSessionKey: sessionKey,
          ownerKey: sessionKey,
          scopeKind: "session",
          label: "Context engine turn maintenance",
          task: "Deferred context-engine maintenance after turn.",
          notifyPolicy: "silent",
          deliveryStatus: "pending",
          preferMetadata: true,
        });

        const maintain = vi.fn(async () => ({
          changed: false,
          bytesFreed: 0,
          rewrittenEntries: 0,
        }));
        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-legacy",
          sessionKey,
          sessionFile: "/tmp/session-legacy.jsonl",
          reason: "turn",
        });

        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));

        const tasks = listTasksForOwnerKey(sessionKey).filter(
          (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        expect(tasks).toHaveLength(2);
        const cancelledLegacyTask = requireRecord(getTaskById(legacyTask.taskId), "legacy task");
        expectRecordFields(cancelledLegacyTask, {
          status: "cancelled",
          notifyPolicy: "silent",
        });
        expect(
          tasks.some(
            (task) => typeof task.runId === "string" && task.runId.startsWith("turn-maint:"),
          ),
        ).toBe(true);
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("cancels the queued task when deferred scheduling is rejected", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      const scheduleError = new Error("gateway draining");
      const enqueueSpy = vi
        .spyOn(commandQueueModule, "enqueueCommandInLane")
        .mockRejectedValue(scheduleError);
      try {
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });
        resetCommandQueueStateForTest();

        const sessionKey = "agent:main:session-enqueue-reject";
        const maintain = vi.fn(async () => ({
          changed: false,
          bytesFreed: 0,
          rewrittenEntries: 0,
        }));
        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-enqueue-reject",
          sessionKey,
          sessionFile: "/tmp/session-enqueue-reject.jsonl",
          reason: "turn",
        });
        await flushAsyncWork();

        const tasks = listTasksForOwnerKey(sessionKey).filter(
          (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        expect(tasks).toHaveLength(1);
        const task = requireRecord(tasks[0], "cancelled task");
        expect(task.status).toBe("cancelled");
        expect(String(task.terminalSummary)).toContain("gateway draining");
        expect(maintain).not.toHaveBeenCalled();
      } finally {
        enqueueSpy.mockRestore();
        vi.useRealTimers();
      }
    });
  });

  it("starts deferred maintenance while the foreground session lane stays busy", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-3";
        const sessionLane = resolveSessionLane(sessionKey);
        const events: string[] = [];
        let releaseFirstForeground: (() => void) | undefined;
        const firstForeground = enqueueCommandInLane(sessionLane, async () => {
          events.push("foreground-1-start");
          await new Promise<void>((resolve) => {
            releaseFirstForeground = resolve;
          });
          events.push("foreground-1-end");
        });
        await Promise.resolve();

        const maintain = vi.fn(async () => {
          events.push("maintenance-start");
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });

        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-3",
          sessionKey,
          sessionFile: "/tmp/session-3.jsonl",
          reason: "turn",
        });

        const secondForeground = enqueueCommandInLane(sessionLane, async () => {
          events.push("foreground-2-start");
          events.push("foreground-2-end");
        });

        await waitForAssertion(() =>
          expect(events).toEqual(["foreground-1-start", "maintenance-start"]),
        );
        expect(maintain).toHaveBeenCalledTimes(1);
        await waitForAssertion(() =>
          expect(
            listTasksForOwnerKey(sessionKey).find(
              (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
            )?.status,
          ).toBe("succeeded"),
        );

        if (!releaseFirstForeground) {
          throw new Error("Expected first foreground release callback to be initialized");
        }
        releaseFirstForeground();
        await waitForAssertion(() =>
          expect(events).toEqual([
            "foreground-1-start",
            "maintenance-start",
            "foreground-1-end",
            "foreground-2-start",
            "foreground-2-end",
          ]),
        );

        await Promise.all([firstForeground, secondForeground]);
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("waits at the same-session read checkpoint before deferred maintenance rewrites", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });

        const sessionKey = "agent:main:session-rewrite-priority";
        const sessionLane = resolveSessionLane(sessionKey);
        const events: string[] = [];
        let allowRewrite: (() => void) | undefined;
        const maintain = vi.fn(async (params?: unknown) => {
          events.push("maintenance-start");
          await new Promise<void>((resolve) => {
            allowRewrite = resolve;
          });
          events.push("maintenance-before-rewrite");
          await (
            params as { runtimeContext?: ContextEngineRuntimeContext }
          ).runtimeContext?.rewriteTranscriptEntries?.({
            replacements: [
              {
                entryId: "entry-1",
                message: castAgentMessage({
                  role: "assistant",
                  content: [{ type: "text", text: "done" }],
                  timestamp: 2,
                }),
              },
            ],
          });
          events.push("maintenance-after-rewrite");
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });

        rewriteTranscriptEntriesInSessionManagerMock.mockImplementationOnce((_params?: unknown) => {
          events.push("rewrite");
          return {
            changed: true,
            bytesFreed: 123,
            rewrittenEntries: 2,
          };
        });

        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-rewrite-priority",
          sessionKey,
          sessionFile: "/tmp/session-rewrite-priority.jsonl",
          reason: "turn",
        });

        await waitForAssertion(() => expect(events).toContain("maintenance-start"));

        const foregroundTurn = enqueueCommandInLane(sessionLane, async () => {
          events.push("foreground-before-read-checkpoint");
          await waitForDeferredTurnMaintenanceForSession(sessionKey);
          events.push("foreground-read");
        });

        if (!allowRewrite) {
          throw new Error("Expected maintenance rewrite release callback to be initialized");
        }
        allowRewrite();

        await waitForAssertion(() =>
          expect(events).toEqual([
            "maintenance-start",
            "foreground-before-read-checkpoint",
            "maintenance-before-rewrite",
            "rewrite",
            "maintenance-after-rewrite",
            "foreground-read",
          ]),
        );

        expect(maintain).toHaveBeenCalledTimes(1);
        await foregroundTurn;
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("keeps fast deferred maintenance silent for the user", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });
        resetSystemEventsForTest();
        const sendMessageMock = vi.fn();
        setTaskRegistryDeliveryRuntimeForTests({
          sendMessage: sendMessageMock,
        });

        const sessionKey = "agent:main:session-fast";
        const maintain = vi.fn(async () => ({
          changed: false,
          bytesFreed: 0,
          rewrittenEntries: 0,
        }));
        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-fast",
          sessionKey,
          sessionFile: "/tmp/session-fast.jsonl",
          reason: "turn",
        });
        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
        expect(sendMessageMock).not.toHaveBeenCalled();
        expect(peekSystemEvents(sessionKey)).toStrictEqual([]);

        const tasks = listTasksForOwnerKey(sessionKey).filter(
          (task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        expect(tasks).toHaveLength(1);
        await waitForAssertion(() =>
          expect(
            getTaskById(expectDefined(tasks[0], "tasks[0] test invariant").taskId)?.status,
          ).toBe("succeeded"),
        );
        const task = requireRecord(
          getTaskById(expectDefined(tasks[0], "tasks[0] test invariant").taskId),
          "maintenance task",
        );
        expectRecordFields(task, {
          status: "succeeded",
          notifyPolicy: "silent",
          deliveryStatus: "not_applicable",
        });
        expect(task.parentFlowId).toBeUndefined();
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("surfaces long-running deferred maintenance and completion via task updates", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });
        resetSystemEventsForTest();

        const sessionKey = "agent:main:session-long";
        let releaseMaintenance: (() => void) | undefined;
        const maintain = vi.fn(async () => {
          await new Promise<void>((resolve) => {
            releaseMaintenance = resolve;
          });
          return {
            changed: false,
            bytesFreed: 0,
            rewrittenEntries: 0,
          };
        });
        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain,
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-long",
          sessionKey,
          sessionFile: "/tmp/session-long.jsonl",
          reason: "turn",
        });

        await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
        await vi.advanceTimersByTimeAsync(11_000);
        await waitForAssertion(() =>
          expectSystemEventContaining(
            sessionKey,
            "Background task update: Context engine turn maintenance.",
          ),
        );
        const task = listTasksForOwnerKey(sessionKey).find(
          (candidate) => candidate.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        const parentFlowId = task?.parentFlowId;
        if (!parentFlowId) {
          throw new Error("Expected visible maintenance to have a task flow");
        }
        expect(getTaskFlowById(parentFlowId)?.status).toBe("running");

        if (!releaseMaintenance) {
          throw new Error("Expected maintenance release callback to be initialized");
        }
        releaseMaintenance();
        await waitForAssertion(() =>
          expectSystemEventContaining(
            sessionKey,
            "Background task done: Context engine turn maintenance",
          ),
        );
        expect(getTaskFlowById(parentFlowId)?.status).toBe("succeeded");
      } finally {
        vi.useRealTimers();
      }
    });
  });

  it("surfaces deferred maintenance failures even when they fail quickly", async () => {
    await withStateDirEnv("openclaw-turn-maintenance-", async () => {
      vi.useFakeTimers();
      try {
        resetCommandQueueStateForTest();
        resetTaskRegistryForTests({ persist: false });
        resetTaskFlowRegistryForTests({ persist: false });
        resetSystemEventsForTest();

        const sessionKey = "agent:main:session-fail";
        const backgroundEngine = {
          info: {
            id: "test",
            name: "Test Engine",
            turnMaintenanceMode: "background" as const,
          },
          ingest: async () => ({ ingested: true }),
          assemble: async ({ messages }: { messages: unknown[] }) => ({
            messages,
            estimatedTokens: 0,
          }),
          compact: async () => ({ ok: true, compacted: false }),
          maintain: vi.fn(async () => {
            throw new Error("maintenance exploded");
          }),
        } as NonNullable<Parameters<typeof runContextEngineMaintenance>[0]["contextEngine"]>;

        await runContextEngineMaintenance({
          contextEngine: backgroundEngine,
          sessionId: "session-fail",
          sessionKey,
          sessionFile: "/tmp/session-fail.jsonl",
          reason: "turn",
        });
        await waitForAssertion(() =>
          expectSystemEventContaining(
            sessionKey,
            "Background task failed: Context engine turn maintenance",
          ),
        );
        const task = listTasksForOwnerKey(sessionKey).find(
          (candidate) => candidate.taskKind === TURN_MAINTENANCE_TASK_KIND,
        );
        const parentFlowId = task?.parentFlowId;
        if (!parentFlowId) {
          throw new Error("Expected failed maintenance to have a task flow");
        }
        expect(getTaskFlowById(parentFlowId)?.status).toBe("failed");
      } finally {
        vi.useRealTimers();
      }
    });
  });
});
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
