TaskDecay v1.0.0 MVP — smart task review & archive Chrome extension
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
export function scoreDecay(task, settings, allTasks) {
|
||||
const now = Date.now();
|
||||
const ageMs = now - task.createdAt.getTime();
|
||||
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
||||
// Priority weight: 1 = lowest, 4 = highest; invert so low-priority tasks decay faster
|
||||
const priorityWeight = (5 - task.priority) * 15; // 15-60 range
|
||||
// Project activity: if many tasks in same project are completed recently, project is active
|
||||
const projectTasks = allTasks.filter(t => t.projectId === task.projectId && t.projectId !== null);
|
||||
const recentProjectActivity = projectTasks.filter(t => t.completedAt && (now - t.completedAt.getTime()) < 30 * 24 * 60 * 60 * 1000).length;
|
||||
const projectActivity = Math.min(30, recentProjectActivity * 5); // 0-30
|
||||
// Label density: more labels = more mental overhead = higher decay
|
||||
const labelDensity = task.labels.length * 8;
|
||||
// Due urgency: if overdue, add urgency; if far future, reduce decay
|
||||
let dueUrgency = 0;
|
||||
if (task.dueDate) {
|
||||
const dueMs = task.dueDate.getTime() - now;
|
||||
if (dueMs < 0) {
|
||||
dueUrgency = 20; // overdue
|
||||
}
|
||||
else if (dueMs < 7 * 24 * 60 * 60 * 1000) {
|
||||
dueUrgency = -10; // due soon, reduce decay
|
||||
}
|
||||
}
|
||||
// Auto-archive: if older than autoArchiveDays, cap at 100
|
||||
let score = Math.round((Math.min(ageDays, settings.autoArchiveDays) / settings.autoArchiveDays) * 50 +
|
||||
priorityWeight +
|
||||
projectActivity +
|
||||
labelDensity +
|
||||
dueUrgency);
|
||||
score = Math.max(0, Math.min(100, score));
|
||||
let explanation = `Age: ${Math.round(ageDays)} days`;
|
||||
if (priorityWeight > 30)
|
||||
explanation += `; Low priority (${task.priority})`;
|
||||
if (projectActivity > 10)
|
||||
explanation += `; Active project`;
|
||||
if (labelDensity > 0)
|
||||
explanation += `; ${task.labels.length} labels`;
|
||||
if (dueUrgency > 0)
|
||||
explanation += `; Overdue`;
|
||||
if (dueUrgency < 0)
|
||||
explanation += `; Due soon`;
|
||||
return {
|
||||
score,
|
||||
ageDays: Math.round(ageDays * 10) / 10,
|
||||
priorityWeight,
|
||||
projectActivity,
|
||||
labelDensity,
|
||||
dueUrgency,
|
||||
explanation,
|
||||
};
|
||||
}
|
||||
export function getReviewTasks(tasks, settings) {
|
||||
const scored = tasks
|
||||
.filter(t => !t.completedAt)
|
||||
.filter(t => {
|
||||
const ageDays = (Date.now() - t.createdAt.getTime()) / (1000 * 60 * 60 * 24);
|
||||
return ageDays >= settings.decayThresholdDays;
|
||||
})
|
||||
.filter(t => !settings.dismissedTasks.includes(t.id))
|
||||
.filter(t => !settings.archivedTasks.includes(t.id))
|
||||
.map(t => ({ task: t, score: scoreDecay(t, settings, tasks) }))
|
||||
.sort((a, b) => b.score.score - a.score.score);
|
||||
return scored.slice(0, settings.maxTasksPerReview);
|
||||
}
|
||||
export function getAutoArchiveCandidates(tasks, settings) {
|
||||
return tasks.filter(t => {
|
||||
if (t.completedAt)
|
||||
return false;
|
||||
const ageDays = (Date.now() - t.createdAt.getTime()) / (1000 * 60 * 60 * 24);
|
||||
return ageDays >= settings.autoArchiveDays && !settings.archivedTasks.includes(t.id);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user