fix(atlas): pause after final verification wave for explicit user approval

This commit is contained in:
YeonGyu-Kim
2026-03-13 12:43:33 +09:00
parent fd71c89b95
commit 04b0c6f33c
9 changed files with 416 additions and 60 deletions
+39 -7
View File
@@ -3,20 +3,28 @@ import { appendSessionId, getPlanProgress, readBoulderState } from "../../featur
import { log } from "../../shared/logger"
import { isCallerOrchestrator } from "../../shared/session-utils"
import { collectGitDiffStats, formatFileChanges } from "../../shared/git-worktree"
import { shouldPauseForFinalWaveApproval } from "./final-wave-approval-gate"
import { HOOK_NAME } from "./hook-name"
import { DIRECT_WORK_REMINDER } from "./system-reminder-templates"
import { isSisyphusPath } from "./sisyphus-path"
import { extractSessionIdFromOutput } from "./subagent-session-id"
import { buildCompletionGate, buildOrchestratorReminder, buildStandaloneVerificationReminder } from "./verification-reminders"
import {
buildCompletionGate,
buildFinalWaveApprovalReminder,
buildOrchestratorReminder,
buildStandaloneVerificationReminder,
} from "./verification-reminders"
import { isWriteOrEditToolName } from "./write-edit-tool-policy"
import type { SessionState } from "./types"
import type { ToolExecuteAfterInput, ToolExecuteAfterOutput } from "./types"
export function createToolExecuteAfterHandler(input: {
ctx: PluginInput
pendingFilePaths: Map<string, string>
autoCommit: boolean
}): (toolInput: ToolExecuteAfterInput, toolOutput: ToolExecuteAfterOutput) => Promise<void> {
const { ctx, pendingFilePaths, autoCommit } = input
getState: (sessionID: string) => SessionState
}): (toolInput: ToolExecuteAfterInput, toolOutput: ToolExecuteAfterOutput) => Promise<void> {
const { ctx, pendingFilePaths, autoCommit, getState } = input
return async (toolInput, toolOutput): Promise<void> => {
// Guard against undefined output (e.g., from /review command - see issue #1035)
if (!toolOutput) {
@@ -75,10 +83,31 @@ export function createToolExecuteAfterHandler(input: {
// Preserve original subagent response - critical for debugging failed tasks
const originalResponse = toolOutput.output
const shouldPauseForApproval = shouldPauseForFinalWaveApproval({
planPath: boulderState.active_plan,
taskOutput: originalResponse,
})
if (toolInput.sessionID) {
const sessionState = getState(toolInput.sessionID)
sessionState.waitingForFinalWaveApproval = shouldPauseForApproval
if (shouldPauseForApproval && sessionState.pendingRetryTimer) {
clearTimeout(sessionState.pendingRetryTimer)
sessionState.pendingRetryTimer = undefined
}
}
const leadReminder = shouldPauseForApproval
? buildFinalWaveApprovalReminder(boulderState.plan_name, progress, subagentSessionId)
: buildCompletionGate(boulderState.plan_name, subagentSessionId)
const followupReminder = shouldPauseForApproval
? null
: buildOrchestratorReminder(boulderState.plan_name, progress, subagentSessionId, autoCommit, false)
toolOutput.output = `
<system-reminder>
${buildCompletionGate(boulderState.plan_name, subagentSessionId)}
${leadReminder}
</system-reminder>
## SUBAGENT WORK COMPLETED
@@ -91,13 +120,16 @@ ${fileChanges}
${originalResponse}
<system-reminder>
${buildOrchestratorReminder(boulderState.plan_name, progress, subagentSessionId, autoCommit, false)}
</system-reminder>`
${
followupReminder === null
? ""
: `<system-reminder>\n${followupReminder}\n</system-reminder>`
}`
log(`[${HOOK_NAME}] Output transformed for orchestrator mode (boulder)`, {
plan: boulderState.plan_name,
progress: `${progress.completed}/${progress.total}`,
fileCount: gitStats.length,
waitingForFinalWaveApproval: shouldPauseForApproval,
})
} else {
toolOutput.output += `\n<system-reminder>\n${buildStandaloneVerificationReminder(subagentSessionId)}\n</system-reminder>`