perf(dashboard): slim task list + auto-archive stale done tasks

GET /api/tasks was returning ~69 MB of JSON per call (67.9 MB of agent
logs across 1199 tasks), causing the dashboard to hang for 2+ minutes.

- core: extend listTasks() with slim and includeArchived options
- dashboard: GET /api/tasks now uses slim mode and excludes archived
  by default; ?includeArchived=1 opts in
- frontend: lazy-load archived tasks when the archived column is first
  expanded via new useTasks.loadArchivedTasks()
- engine: self-healing maintenance now auto-archives done tasks older
  than 48h (data stays in SQLite, column flips done -> archived)
- tests: slim mode + includeArchived coverage in store.test.ts;
  routes.test.ts assertion updated for new args

Also bundles in-progress test-setup noise filters and pre-existing
QuickEntryBox/routes test work that was already modified locally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-10 20:16:20 -07:00
parent c9a1e94bbe
commit 5984584d22
11 changed files with 333 additions and 82 deletions

View File

@@ -344,6 +344,7 @@ export class SelfHealingManager {
await this.recoverMisclassifiedFailures();
await this.recoverOrphanedExecutions();
await this.recoverApprovedTriageTasks();
await this.archiveStaleDoneTasks();
const elapsedMs = Date.now() - startMs;
log.log(`Maintenance cycle completed in ${elapsedMs}ms`);
@@ -352,6 +353,55 @@ export class SelfHealingManager {
}
}
// ── Auto-archive of stale done tasks ──────────────────────────────
/**
* Auto-archive done tasks older than 48 hours so the dashboard board view
* stops accumulating thousands of completed tasks. Data remains in SQLite —
* the task is moved from `done` to `archived`, which the slim list endpoint
* excludes by default. Users can still expand the archived column or unarchive.
*/
private static readonly AUTO_ARCHIVE_AFTER_MS = 48 * 60 * 60 * 1000;
async archiveStaleDoneTasks(): Promise<number> {
try {
const tasks = await this.store.listTasks();
const cutoff = Date.now() - SelfHealingManager.AUTO_ARCHIVE_AFTER_MS;
const stale = tasks.filter((t) => {
if (t.column !== "done") return false;
// Prefer columnMovedAt (when the task entered done); fall back to updatedAt
// for legacy tasks that lack the field.
const ts = t.columnMovedAt || t.updatedAt;
const movedAt = ts ? Date.parse(ts) : NaN;
if (!Number.isFinite(movedAt)) return false;
return movedAt < cutoff;
});
if (stale.length === 0) return 0;
log.log(`Auto-archiving ${stale.length} done task(s) older than 48h`);
let archived = 0;
for (const task of stale) {
try {
await this.store.archiveTask(task.id);
archived++;
} catch (err: any) {
log.error(`Failed to auto-archive ${task.id}: ${err.message}`);
}
}
if (archived > 0) {
log.log(`Auto-archived ${archived} stale done task(s)`);
}
return archived;
} catch (err: any) {
log.error(`Auto-archive sweep failed: ${err.message}`);
return 0;
}
}
// ── Completed task recovery ──────────────────────────────────────
/**