Fix silent context-engine maintenance task delivery status (#102147)

* Fix silent context maintenance task delivery status

* refactor(context-engine): document maintenance delivery invariant

* fix(context-engine): preserve visible maintenance flows

* style(context-engine): format maintenance test import

* test(context-engine): use direct maintenance task lookup

---------

Co-authored-by: Sascha Kuhlmann <coolmanns@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Sascha Kuhlmann 2026-07-09 09:33:05 -05:00 committed by GitHub
parent 3ebcfb8ee3
commit 13821c9d34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 50 additions and 46 deletions

View file

@ -9,7 +9,7 @@ import {
} from "../../process/command-queue.js"; } from "../../process/command-queue.js";
import * as commandQueueModule from "../../process/command-queue.js"; import * as commandQueueModule from "../../process/command-queue.js";
import { createQueuedTaskRun as createQueuedTaskRunOrNull } from "../../tasks/task-executor.js"; import { createQueuedTaskRun as createQueuedTaskRunOrNull } from "../../tasks/task-executor.js";
import { resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js"; import { getTaskFlowById, resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js";
import { import {
getTaskById, getTaskById,
listTasksForOwnerKey, listTasksForOwnerKey,
@ -654,7 +654,7 @@ describe("runContextEngineMaintenance", () => {
requesterSessionKey: sessionKey, requesterSessionKey: sessionKey,
taskKind: TURN_MAINTENANCE_TASK_KIND, taskKind: TURN_MAINTENANCE_TASK_KIND,
notifyPolicy: "silent", notifyPolicy: "silent",
deliveryStatus: "pending", deliveryStatus: "not_applicable",
}); });
if (!releaseForeground) { if (!releaseForeground) {
@ -1494,6 +1494,21 @@ describe("runContextEngineMaintenance", () => {
await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1)); await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
expect(sendMessageMock).not.toHaveBeenCalled(); expect(sendMessageMock).not.toHaveBeenCalled();
expect(peekSystemEvents(sessionKey)).toStrictEqual([]); expect(peekSystemEvents(sessionKey)).toStrictEqual([]);
const tasks = listTasksForOwnerKey(sessionKey).filter(
(task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
);
expect(tasks).toHaveLength(1);
await waitForAssertion(() =>
expect(getTaskById(tasks[0].taskId)?.status).toBe("succeeded"),
);
const task = requireRecord(getTaskById(tasks[0].taskId), "maintenance task");
expectRecordFields(task, {
status: "succeeded",
notifyPolicy: "silent",
deliveryStatus: "not_applicable",
});
expect(task.parentFlowId).toBeUndefined();
} finally { } finally {
vi.useRealTimers(); vi.useRealTimers();
} }
@ -1552,6 +1567,14 @@ describe("runContextEngineMaintenance", () => {
"Background task update: Context engine turn maintenance.", "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) { if (!releaseMaintenance) {
throw new Error("Expected maintenance release callback to be initialized"); throw new Error("Expected maintenance release callback to be initialized");
@ -1563,6 +1586,7 @@ describe("runContextEngineMaintenance", () => {
"Background task done: Context engine turn maintenance", "Background task done: Context engine turn maintenance",
), ),
); );
expect(getTaskFlowById(parentFlowId)?.status).toBe("succeeded");
} finally { } finally {
vi.useRealTimers(); vi.useRealTimers();
} }
@ -1609,6 +1633,14 @@ describe("runContextEngineMaintenance", () => {
"Background task failed: Context engine turn maintenance", "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 { } finally {
vi.useRealTimers(); vi.useRealTimers();
} }

View file

@ -22,7 +22,6 @@ import {
createQueuedTaskRun, createQueuedTaskRun,
failTaskRunByRunId, failTaskRunByRunId,
recordTaskRunProgressByRunId, recordTaskRunProgressByRunId,
setDetachedTaskDeliveryStatusByRunId,
startTaskRunByRunId, startTaskRunByRunId,
} from "../../tasks/detached-task-runtime.js"; } from "../../tasks/detached-task-runtime.js";
import { import {
@ -231,11 +230,15 @@ function markDeferredTurnMaintenanceTaskScheduleFailure(params: {
}); });
} }
function buildTurnMaintenanceTaskDescriptor(params: { sessionKey: string }) { function buildTurnMaintenanceTaskDescriptor(params: {
const runId = `turn-maint:${params.sessionKey}:${Date.now().toString(36)}:${randomUUID().slice( sessionKey: string;
0, runId?: string;
8, notifyPolicy?: "silent" | "done_only" | "state_changes";
)}`; deliveryStatus?: "not_applicable" | "pending";
}) {
const runId =
params.runId ??
`turn-maint:${params.sessionKey}:${Date.now().toString(36)}:${randomUUID().slice(0, 8)}`;
return createQueuedTaskRun({ return createQueuedTaskRun({
runtime: "acp", runtime: "acp",
taskKind: TURN_MAINTENANCE_TASK_KIND, taskKind: TURN_MAINTENANCE_TASK_KIND,
@ -246,8 +249,10 @@ function buildTurnMaintenanceTaskDescriptor(params: { sessionKey: string }) {
runId, runId,
label: TURN_MAINTENANCE_TASK_LABEL, label: TURN_MAINTENANCE_TASK_LABEL,
task: TURN_MAINTENANCE_TASK_TASK, task: TURN_MAINTENANCE_TASK_TASK,
notifyPolicy: "silent", notifyPolicy: params.notifyPolicy ?? "silent",
deliveryStatus: "pending", // Fast maintenance stays silent and must not create a one-task flow.
// Long-running and failed workers promote it to pending before notifying.
deliveryStatus: params.deliveryStatus ?? "not_applicable",
preferMetadata: true, preferMetadata: true,
}); });
} }
@ -257,45 +262,12 @@ function promoteTurnMaintenanceTaskVisibility(params: {
runId: string; runId: string;
notifyPolicy: "done_only" | "state_changes"; notifyPolicy: "done_only" | "state_changes";
}) { }) {
const task = findTaskByRunIdForOwner({ return buildTurnMaintenanceTaskDescriptor({
runId: params.runId,
callerOwnerKey: params.sessionKey,
});
if (!task) {
return createQueuedTaskRun({
runtime: "acp",
taskKind: TURN_MAINTENANCE_TASK_KIND,
sourceId: TURN_MAINTENANCE_TASK_KIND,
requesterSessionKey: params.sessionKey,
ownerKey: params.sessionKey,
scopeKind: "session",
runId: params.runId,
label: TURN_MAINTENANCE_TASK_LABEL,
task: TURN_MAINTENANCE_TASK_TASK,
notifyPolicy: params.notifyPolicy,
deliveryStatus: "pending",
preferMetadata: true,
});
}
setDetachedTaskDeliveryStatusByRunId({
runId: params.runId,
runtime: "acp",
sessionKey: params.sessionKey, sessionKey: params.sessionKey,
runId: params.runId,
notifyPolicy: params.notifyPolicy,
deliveryStatus: "pending", deliveryStatus: "pending",
}); });
if (task.notifyPolicy !== params.notifyPolicy) {
updateTaskNotifyPolicyForOwner({
taskId: task.taskId,
callerOwnerKey: params.sessionKey,
notifyPolicy: params.notifyPolicy,
});
}
return (
findTaskByRunIdForOwner({
runId: params.runId,
callerOwnerKey: params.sessionKey,
}) ?? task
);
} }
/** /**