Skip to content

Commit 09c5999

Browse files
committed
fix(server,web,mobile): failed idle compaction never gates the queue or recovery
1 parent 54de9a2 commit 09c5999

4 files changed

Lines changed: 21 additions & 5 deletions

File tree

‎apps/server/src/orchestration-v2/IdleCompactionService.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ export const make = Effect.gen(function* () {
7373
);
7474
if (current === null || current.runId !== candidate.runId) return;
7575
const instance = yield* instances.getInstance(projection.thread.providerInstanceId);
76-
if (instance === undefined || !instance.enabled || instance.driverKind !== current.driver)
77-
return;
76+
// Enablement is the project-resolved value above; a project can enable
77+
// an instance that is disabled machine-wide.
78+
if (instance === undefined || instance.driverKind !== current.driver) return;
7879
// Earlier candidates in this sweep can push this one past its cache TTL.
7980
if (DateTime.toEpochMillis(yield* DateTime.now) >= window.endMs) return;
8081
// One idempotent command per completed run survives concurrent scans and

‎apps/server/src/orchestration-v2/runtimeLayer.test.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4136,6 +4136,11 @@ it.layer(layerTest)("OrchestrationV2LayerLive lifecycle", (it) => {
41364136
],
41374137
});
41384138

4139+
// A failed idle compaction is maintenance: the queued message still runs.
4140+
if (idleCompaction) {
4141+
assert.equal(yield* Queue.take(promotedRunIds), queuedRun.id);
4142+
return;
4143+
}
41394144
if (failureClass === "provider_error") {
41404145
assert.equal(yield* Queue.take(heldRunIds), queuedRun.id);
41414146
const held = yield* orchestrator.getThreadProjection(threadId);
@@ -4153,7 +4158,6 @@ it.layer(layerTest)("OrchestrationV2LayerLive lifecycle", (it) => {
41534158
const after = yield* orchestrator.getThreadProjection(threadId);
41544159
assert.equal(after.runs.find((run) => run.id === queuedRun.id)?.status, "queued");
41554160
assert.isFalse(after.turnItems.some((item) => item.runId === queuedRun.id));
4156-
if (idleCompaction) return;
41574161
const shell = (yield* orchestrator.getShellSnapshot()).threads.find(
41584162
(thread) => thread.id === threadId,
41594163
);

‎packages/client-runtime/src/state/threadExecution.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,17 @@ function presentedUsageLimitRun(
7272
const providerSession = projection.providerSessions.findLast(
7373
(session) => session.providerInstanceId === projection.thread.providerInstanceId,
7474
);
75+
// Mirrors the server shell: idle compaction never stands for the outcome.
76+
const latestPhysicalRun = projection.runs.reduce<(typeof projection.runs)[number] | null>(
77+
(latest, run) => (latest === null || run.ordinal > latest.ordinal ? run : latest),
78+
null,
79+
);
7580
return usageLimitRunPresentedAsLatest(
76-
projection.runs,
81+
projection.runs.filter((run) => run.idleCompactionOfRunId === undefined),
7782
projection.turnItems,
78-
providerSession?.lastError ?? null,
83+
latestPhysicalRun?.idleCompactionOfRunId !== undefined
84+
? null
85+
: (providerSession?.lastError ?? null),
7986
);
8087
}
8188

‎packages/shared/src/orchestrationV2ThreadError.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ export function latestExecutedRun(
5353
): OrchestrationV2Run | null {
5454
let latest: OrchestrationV2Run | null = null;
5555
for (const run of runs) {
56+
// A failed idle compaction is invisible maintenance: it neither presents as
57+
// the outcome nor holds the queue. A queued message that then meets the same
58+
// usage limit fails as an ordinary run with the normal recovery controls.
59+
if (run.idleCompactionOfRunId !== undefined) continue;
5660
if (run.status === "queued") continue;
5761
if (run.status === "cancelled" && run.startedAt === null) continue;
5862
if (latest === null || runRanAfter(run, latest)) latest = run;

0 commit comments

Comments
 (0)