fix(atlas): gate final-wave approval on real plan state

Ignore nested plan checkboxes and track parallel final-wave approvals so Atlas only pauses for user approval when the real top-level review wave is complete.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-03-14 13:47:40 +09:00
parent 1ad5db4e8b
commit 16b0d9eb77
10 changed files with 336 additions and 46 deletions
+32 -32
View File
@@ -1,47 +1,47 @@
import { existsSync, readFileSync } from "node:fs"
import type { SessionState } from "./types"
import { readFinalWavePlanState } from "./final-wave-plan-state"
const APPROVE_VERDICT_PATTERN = /\bVERDICT:\s*APPROVE\b/i
const FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i
const UNCHECKED_TASK_PATTERN = /^\s*[-*]\s*\[\s*\]\s*(.+)$/
const FINAL_WAVE_TASK_PATTERN = /^F\d+\./i
function clearFinalWaveApprovalTracking(sessionState: SessionState): void {
sessionState.pendingFinalWaveTaskCount = undefined
sessionState.approvedFinalWaveTaskCount = undefined
}
export function shouldPauseForFinalWaveApproval(input: {
planPath: string
taskOutput: string
sessionState: SessionState
}): boolean {
const planState = readFinalWavePlanState(input.planPath)
if (!planState) {
return false
}
if (planState.pendingImplementationTaskCount > 0 || planState.pendingFinalWaveTaskCount === 0) {
clearFinalWaveApprovalTracking(input.sessionState)
return false
}
if (!APPROVE_VERDICT_PATTERN.test(input.taskOutput)) {
return false
}
if (!existsSync(input.planPath)) {
return false
if (planState.pendingFinalWaveTaskCount === 1) {
clearFinalWaveApprovalTracking(input.sessionState)
return true
}
try {
const content = readFileSync(input.planPath, "utf-8")
const lines = content.split(/\r?\n/)
let inFinalVerificationWave = false
let uncheckedTaskCount = 0
let uncheckedFinalWaveTaskCount = 0
for (const line of lines) {
if (/^##\s+/.test(line)) {
inFinalVerificationWave = FINAL_VERIFICATION_HEADING_PATTERN.test(line)
}
const uncheckedTaskMatch = line.match(UNCHECKED_TASK_PATTERN)
if (!uncheckedTaskMatch) {
continue
}
uncheckedTaskCount += 1
if (inFinalVerificationWave && FINAL_WAVE_TASK_PATTERN.test(uncheckedTaskMatch[1].trim())) {
uncheckedFinalWaveTaskCount += 1
}
}
return uncheckedTaskCount === 1 && uncheckedFinalWaveTaskCount === 1
} catch {
return false
if (input.sessionState.pendingFinalWaveTaskCount !== planState.pendingFinalWaveTaskCount) {
input.sessionState.pendingFinalWaveTaskCount = planState.pendingFinalWaveTaskCount
input.sessionState.approvedFinalWaveTaskCount = 0
}
input.sessionState.approvedFinalWaveTaskCount = (input.sessionState.approvedFinalWaveTaskCount ?? 0) + 1
const shouldPause = input.sessionState.approvedFinalWaveTaskCount >= planState.pendingFinalWaveTaskCount
if (shouldPause) {
clearFinalWaveApprovalTracking(input.sessionState)
}
return shouldPause
}