fix(auto): block refusing executor model temporarily to force escalation on retry

When classifyExecutorRefusal detects an executor refusal, the model is
now temporarily blocked (1-hour TTL) via the existing blocked-models
mechanism. This ensures that on retry — whether automatic or manual —
the router skips the refusing model and the tier-escalation path in
selectAndApplyModel picks a higher-tier alternative.

This satisfies AC1 of self-feedback entry sf-mp3bm6u0-2fskt8.
AC2 (refusal pattern detection) was already satisfied by the existing
apology-no-tools pattern in classifyExecutorRefusal.

Refs: sf-mp3bm6u0-2fskt8
This commit is contained in:
Mikael Hugo 2026-05-13 02:40:41 +02:00
parent 2cad6d54f4
commit 3f2babb5d1

View file

@ -38,6 +38,7 @@ import {
readAutonomousSolverState,
recordAutonomousSolverMissingCheckpointRetry,
} from "../autonomous-solver.js";
import { blockModel } from "../blocked-models.js";
import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
import { debugLog } from "../debug-logger.js";
import { PROJECT_FILES } from "../detection.js";
@ -759,12 +760,30 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
} catch {
// best-effort
}
// Temporarily block the refusing model so the router skips it on retry.
// This satisfies AC1 of sf-mp3bm6u0-2fskt8: the executor model is
// escalated because the blocked model will be excluded from selection.
try {
const refusedProvider = s.currentUnitModel?.provider ?? "";
const refusedId = s.currentUnitModel?.id ?? "";
if (refusedProvider && refusedId) {
blockModel(
s.basePath,
refusedProvider,
refusedId,
`executor-refused: ${refusal.pattern}`,
{ expiresAt: Date.now() + 60 * 60 * 1000 }, // 1 hour
);
}
} catch {
// best-effort — blocking must not break the refusal handler
}
try {
appendAutonomousSolverCheckpoint(s.basePath, {
unitType,
unitId,
outcome: "blocked",
summary: `Executor (${executorModel}) refused the task. Pattern: ${refusal.pattern}. Repair-prompting the same model cannot produce progress; escalate the executor model or unblock this unit manually.`,
summary: `Executor (${executorModel}) refused the task. Pattern: ${refusal.pattern}. The model has been temporarily blocked and will be skipped on retry; escalate the executor model or unblock this unit manually.`,
completedItems: [],
remainingItems: [
`Re-run ${unitType} ${unitId} with a more capable executor model — current routing selected an incapable model.`,