Skip to content
Merged
12 changes: 11 additions & 1 deletion apps/server/src/orchestration-v2/Orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4476,10 +4476,16 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
cause: "The reset time has passed. Retry the thread manually.",
});
}
if (thread.settledOverride === "settled") {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return yield* new OrchestratorDispatchError({
commandId: command.commandId,
commandType: command.type,
cause: "The thread is settled. Un-settle it to resume at the reset.",
});
}
if (
!Number.isFinite(resetMs) ||
thread.archivedAt !== null ||
thread.settledOverride === "settled" ||
run?.id !== command.limitRecovery.runId ||
failure?.class !== "usage_limit" ||
failure.resetAt !== command.limitRecovery.resetAt ||
Expand Down Expand Up @@ -4599,6 +4605,10 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
...thread,
settledOverride: "settled",
settledAt: alreadySettled ? thread.settledAt : (command.settledAt ?? now),
// Settled threads refuse recovery, so an armed resume would stay hidden until un-settled.
limitRecovery: thread.limitRecovery
? { ...thread.limitRecovery, autoResume: false, snooze: false }
: thread.limitRecovery,
pullRequests: thread.pullRequests?.map((link) => withPullRequestWatch(link, undefined)),
unsettledAt: null,
pinnedAt: null,
Expand Down
33 changes: 33 additions & 0 deletions apps/server/src/orchestration-v2/runtimeLayer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10866,6 +10866,7 @@ it.layer(layerTest)("usage-limit recovery", (it) => {
"replacement",
"manual-snooze",
"invalid-snooze",
"settled",
"cancel-resume-keep-snooze",
"independent-patches",
"expired-snooze",
Expand Down Expand Up @@ -11019,6 +11020,38 @@ it.layer(layerTest)("usage-limit recovery", (it) => {
assert.isNull(current.thread.snoozedUntil);
return;
}
if (scenario === "settled") {
yield* orchestrator.dispatch({
type: "thread.metadata.update",
commandId: CommandId.make("recovery:arm-before-settle:settled"),
threadId,
limitRecovery: { runId: run.id, resetAt, autoResume: true, snooze: true },
});
yield* orchestrator.dispatch({
type: "thread.settle",
commandId: CommandId.make("recovery:settle:settled"),
threadId,
});
const error = yield* orchestrator
.dispatch({
type: "thread.metadata.update",
commandId: CommandId.make("recovery:arm:settled"),
threadId,
limitRecovery: { runId: run.id, resetAt, autoResume: true },
})
.pipe(Effect.flip);
assert(error._tag === "OrchestratorDispatchError");
assert.equal(error.cause, "The thread is settled. Un-settle it to resume at the reset.");
assert.deepInclude(
(yield* orchestrator.getThreadProjection(threadId)).thread.limitRecovery,
{
runId: run.id,
autoResume: false,
snooze: false,
},
);
return;
}
const snooze = scenario === "wake" || scenario === "cancel-resume-keep-snooze";
const autoResume = scenario !== "wake";
const arm = limitRecoveryCommand(shell, autoResume, DateTime.toEpochMillis(now), snooze);
Expand Down
Loading