fix: resume selection under-reports task totals with multiple loops
The resume prompt counted total/completed from progress.tasks, which only records TOUCHED tasks (started/completed/failed) — never-started tasks were silently missing, and file-checkbox completions weren't counted unless markCompleted had run. With e.g. 10 tasks where 3 completed and 5 untouched, it showed 3/5 done instead of 3/10 done. - add countPRDResumeStats (src/utils.ts): total from parseTaskFile of the PRD source; completed = progress completions ∪ PRD checkbox completions, deduped by task id; failed from progress. Falls back to touched-task counts when the source file is missing/unparseable. - selectPRDToResume (index.ts) uses the helper instead of Object.keys(). - tests/resume-stats.test.ts: 4 regression tests. Includes pre-existing uncommitted changes: index.ts tab-reformat (matches src/ style) and package.json version bump 0.4.2 -> 0.4.3.
This commit is contained in:
44
src/utils.ts
44
src/utils.ts
@@ -7,6 +7,7 @@ import type {
|
||||
ToolUsage,
|
||||
} from "./types";
|
||||
import { DEFAULT_CONFIG } from "./types";
|
||||
import { parseTaskFile } from "./parser";
|
||||
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
||||
import {
|
||||
createAgentSession,
|
||||
@@ -198,6 +199,49 @@ export function listPRDsSorted(
|
||||
return entries;
|
||||
}
|
||||
|
||||
export interface PRDResumeSummary {
|
||||
total: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count tasks for the resume-selection display.
|
||||
*
|
||||
* The progress tracker only records tasks that were TOUCHED (started,
|
||||
* completed, or failed) — never-started tasks are absent from `prd.tasks`,
|
||||
* so a naive Object.keys(prd.tasks).length under-reports the real total.
|
||||
* The true total comes from parsing the PRD source file. Completed counts
|
||||
* both progress-marked completions and PRD checkbox completions (a task
|
||||
* checked off in the file is done even if the loop was interrupted before
|
||||
* markCompleted), deduped by task id. Falls back to touched-task counts
|
||||
* when the source file is missing or unparseable.
|
||||
*/
|
||||
export function countPRDResumeStats(
|
||||
prd: PRDProgress,
|
||||
sourcePath: string,
|
||||
): PRDResumeSummary {
|
||||
const touched = Object.entries(prd.tasks);
|
||||
const failed = touched.filter(([, t]) => t.status === "failed").length;
|
||||
const completedIds = new Set(
|
||||
touched.filter(([, t]) => t.status === "completed").map(([id]) => id),
|
||||
);
|
||||
|
||||
let total: number;
|
||||
try {
|
||||
const project = parseTaskFile(sourcePath);
|
||||
total = project.tasks.length;
|
||||
for (const task of project.tasks) {
|
||||
if (task.status === "completed") completedIds.add(task.id);
|
||||
}
|
||||
} catch {
|
||||
// PRD file missing/unparseable — fall back to touched-task counts
|
||||
total = touched.length;
|
||||
}
|
||||
|
||||
return { total, completed: completedIds.size, failed };
|
||||
}
|
||||
|
||||
// ─── Model Resolution ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user