fix(agent-pod): committed progress ledger so resume skips done tasks
Resuming the phase branch alone only recovers the code -- the task loop still walked from the first task, re-verifying every already-done one through a full planner call before reaching the first task that actually needed work. .agent-progress is committed (not gitignored) and appended per completed task, so a resumed run reads it once and skips straight past known-done tasks with zero LLM calls. Validated locally against a throwaway repo: second run skipped both tasks instantly (resumed: true) instead of re-running planner on them.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user