feat: sweep all sections for already-done tasks before picking new work
This commit is contained in:
@@ -89,17 +89,13 @@ export class TaskLoop {
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info("task-loop", "Polling for Asana tasks...");
|
||||
// First: sweep all sections for tasks that are already done
|
||||
await this.sweepDoneTasks();
|
||||
|
||||
// Search TODO sections in priority order
|
||||
let tasks: AsanaTask[] = [];
|
||||
for (const sectionName of SECTIONS.TODO) {
|
||||
tasks = await this.asana.getIncompleteTasks(this.config.asanaProjectGid, sectionName);
|
||||
if (tasks.length > 0) {
|
||||
logger.info("task-loop", `Found ${tasks.length} task(s) in "${sectionName}"`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Then: pick up new tasks to execute
|
||||
logger.info("task-loop", "Polling for new Asana tasks...");
|
||||
|
||||
let tasks = await this.getTasksFromSections(this.config.asanaProjectGid, SECTIONS.TODO);
|
||||
|
||||
if (tasks.length === 0) {
|
||||
logger.info("task-loop", "No pending tasks found");
|
||||
@@ -260,6 +256,69 @@ EVIDENCE: one line why`;
|
||||
}
|
||||
}
|
||||
|
||||
private async sweepDoneTasks() {
|
||||
try {
|
||||
// Get all incomplete tasks from all active sections (not just TODO)
|
||||
const sweepSections = [...SECTIONS.TODO, ...SECTIONS.IN_PROGRESS];
|
||||
const allTasks: AsanaTask[] = [];
|
||||
|
||||
for (const sectionName of sweepSections) {
|
||||
const section = await this.asana.findSection(this.config.asanaProjectGid, sectionName);
|
||||
if (!section) continue;
|
||||
const tasks = await this.asana.getSectionTasks(section.gid);
|
||||
const incomplete = tasks.filter((t) => !t.completed);
|
||||
allTasks.push(...incomplete);
|
||||
}
|
||||
|
||||
if (allTasks.length === 0) return;
|
||||
|
||||
// Deduplicate by GID
|
||||
const seen = new Set<string>();
|
||||
const uniqueTasks = allTasks.filter((t) => {
|
||||
if (seen.has(t.gid)) return false;
|
||||
seen.add(t.gid);
|
||||
return true;
|
||||
});
|
||||
|
||||
logger.info("task-loop", `Sweeping ${uniqueTasks.length} incomplete tasks for already-done work`);
|
||||
|
||||
for (const task of uniqueTasks) {
|
||||
const check = await this.verifyTaskAlreadyDone(task);
|
||||
|
||||
if (check.done) {
|
||||
logger.info("task-loop", `Sweep: "${task.name}" is already done`, { evidence: check.evidence });
|
||||
|
||||
await this.asana.addComment(
|
||||
task.gid,
|
||||
`✅ Pi Worker sweep: This task is **already completed** on the server.\n\nEvidence:\n${check.evidence}`
|
||||
);
|
||||
|
||||
const doneSection = await this.findFirstSection(SECTIONS.DONE);
|
||||
if (doneSection) {
|
||||
await this.asana.moveTaskToSection(task.gid, doneSection.gid);
|
||||
}
|
||||
await this.asana.updateTask(task.gid, { completed: true });
|
||||
logger.info("task-loop", `Sweep: Marked done: ${task.name}`);
|
||||
} else {
|
||||
logger.debug("task-loop", `Sweep: "${task.name}" not yet done`, { evidence: check.evidence });
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error("task-loop", `Sweep error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async getTasksFromSections(projectGid: string, sectionNames: string[]): Promise<AsanaTask[]> {
|
||||
for (const name of sectionNames) {
|
||||
const section = await this.asana.findSection(projectGid, name);
|
||||
if (!section) continue;
|
||||
const tasks = await this.asana.getSectionTasks(section.gid);
|
||||
const incomplete = tasks.filter((t) => !t.completed);
|
||||
if (incomplete.length > 0) return incomplete;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private buildTaskContext(task: AsanaTask): string {
|
||||
const parts = [
|
||||
`Asana Task: ${task.name}`,
|
||||
|
||||
Reference in New Issue
Block a user