diff --git a/k8s/apps/agent-pod/coordinator-configmap.yaml b/k8s/apps/agent-pod/coordinator-configmap.yaml index 2eb5069..04324da 100644 --- a/k8s/apps/agent-pod/coordinator-configmap.yaml +++ b/k8s/apps/agent-pod/coordinator-configmap.yaml @@ -469,6 +469,36 @@ data: } } + // Committed (never gitignored) so it survives a resumed phase branch -- + // one task id per line, appended as each task resolves. This is what lets + // a resumed run skip straight past already-resolved tasks instead of + // re-running planner's judgeOnly decision on every one of them again: + // resuming the git branch alone only recovers the CODE, not "which tasks + // are already settled," and re-deciding that from scratch for every task + // burns a full LLM call per already-done task before ever reaching the + // first one that actually needs work. + function progressLedgerPath(cwd) { + return path.join(cwd, ".agent-progress"); + } + + function readCompletedTasks(cwd) { + const file = progressLedgerPath(cwd); + if (!fs.existsSync(file)) return new Set(); + return new Set( + fs + .readFileSync(file, "utf8") + .split("\n") + .map((line) => line.trim()) + .filter(Boolean) + ); + } + + async function recordTaskComplete(cwd, task) { + fs.appendFileSync(progressLedgerPath(cwd), `${task}\n`); + await runGit(cwd, ["add", path.basename(progressLedgerPath(cwd))]); + await runGit(cwd, ["commit", "-m", `chore: mark ${task} complete in progress ledger`]); + } + // Runs every task in a phase (no declared dependency between them) strictly // one at a time against the repo's shared role pool -- only one implementer/ // judge/etc. exists per repo, so there is no per-task concurrency to have @@ -485,9 +515,21 @@ data: // from a clone that never saw any of that work. async function runPhase(cwd, baseBranch, phaseBranch, phaseTasks, pool, repoId, pipelineSession) { const entries = phaseTasks.map((t) => (typeof t === "string" ? { id: t, judgeOnly: false } : t)); + const completed = readCompletedTasks(cwd); + for (const entry of entries) { + if (completed.has(entry.id)) { + const result = { task: entry.id, status: "done", resumed: true }; + pipelineSession.taskResults.push(result); + logProgress(pipelineSession); + continue; + } + const result = await runTaskOnPool(cwd, baseBranch, entry.id, pool, repoId, pipelineSession, entry.judgeOnly); pipelineSession.taskResults.push(result); + if (result.status === "done" || result.status === "done-with-concerns") { + await recordTaskComplete(cwd, entry.id); + } await runGit(cwd, ["push", "-u", "origin", phaseBranch]); logProgress(pipelineSession); }