callumalpass_tasknotes/issue-analysis/issue-810.md
callumalpass 6cbdb23d69 analysis: AI analysis for issue #810
[Bug]: Agenda not showing overdue tasks

Generated by ai-issue-analyzer
2025-10-06 21:05:29 +11:00

10 KiB

Issue #810 Analysis: Agenda View Not Showing Overdue Tasks

Problem Understanding

Issue Title: [Bug]: Agenda not showing overdue tasks

Reporter: skyrunner15

Description: When clicking on "today" in the agenda view, it properly filters out prior days but overdue tasks disappear. The user can see the missing task in the Tasks view with a due date of yesterday but not showing in the agenda view. When updating the due date to today, it doesn't show in the agenda view either. The task is a recurring one.

Key Observations:

  1. Overdue tasks disappear when viewing "today" in the agenda view
  2. Tasks view shows the overdue task correctly (due date = yesterday)
  3. Updating task due date to today doesn't make it appear
  4. The task is recurring, which may be relevant to the bug

Test File Location

A test file has been created but is currently failing:

  • Path: tests/unit/issues/issue-810-overdue-tasks-disappear.test.ts
  • Test Framework: Jest
  • Current Status: FAILING (missing test helper functions)
  • Run Command: npm test -- issue-810-overdue-tasks-disappear.test.ts

Test Failure Reason:

The test is trying to use createMockCacheManager() and createMockTaskInfo() helper functions which don't exist in the test helpers. The test needs to be updated to use the existing factory methods from tests/helpers/mock-factories.ts:

  • Use TaskFactory.createTask() instead of createMockTaskInfo()
  • Use PluginFactory.createMockPlugin().cacheManager instead of createMockCacheManager()

Relevant Code Locations

1. AgendaView Component

File: src/views/AgendaView.ts

Key Lines:

  • AgendaView.ts:731-735 - Calls getAgendaDataWithOverdue() method
  • AgendaView.ts:843-1018 - Renders overdue section separately
  • AgendaView.ts:65 - showOverdueOnToday property controls overdue display

2. FilterService - Main Logic

File: src/services/FilterService.ts

Key Functions:

a. getAgendaDataWithOverdue()

  • Location: FilterService.ts:2622-2652
  • Purpose: Gets agenda data with a separate overdue section
  • Parameters:
    • dates: Date[] - Array of dates to get tasks for
    • baseQuery: FilterQuery - Filter query to apply
    • showOverdueSection: boolean - Whether to show overdue section
  • Logic:
    // Line 2630-2642: Gets tasks for each specific date (no overdue mixing)
    for (const date of dates) {
      const tasksForDate = await this.getTasksForDate(
        date,
        baseQuery,
        false // Never include overdue in daily sections
      );
      dailyData.push({ date: new Date(date), tasks: tasksForDate });
    }
    
    // Line 2645-2646: Gets overdue tasks separately if requested
    const overdueTasks = showOverdueSection ? await this.getOverdueTasks(baseQuery) : [];
    

b. getOverdueTasks()

  • Location: FilterService.ts:2573-2617
  • Purpose: Get overdue tasks for the agenda view
  • Logic:
    • Line 2579: Filters all tasks to find overdue ones
    • Line 2584-2591: For recurring tasks, only checks scheduled date
    • Line 2593-2606: For non-recurring tasks, checks both due and scheduled dates
    • Line 2588, 2596, 2603: Uses isOverdueTimeAware() helper function

c. isOverdueTimeAware()

  • Location: src/utils/dateUtils.ts:1021-1050
  • Purpose: Check if a date string represents an overdue date
  • Parameters:
    • dateString: string - The date to check
    • isCompleted?: boolean - Whether the task is completed
    • hideCompletedFromOverdue?: boolean - Setting to hide completed tasks from overdue
  • Logic:
    • Line 1029-1031: If setting enabled and task completed, return false
    • Line 1037-1044: Uses UTC anchors to compare dates consistently

Root Cause Analysis

Based on the code review, the issue appears to be in how recurring tasks are handled in the getOverdueTasks() method:

For Recurring Tasks (Line 2584-2591):

if (task.recurrence) {
  // Only check scheduled date for recurring tasks (this is the current instance date)
  if (task.scheduled) {
    return isOverdueTimeAware(task.scheduled, isCompleted, hideCompletedFromOverdue);
  }
  return false;
}

Problem: If a recurring task's scheduled date has been updated to today (or is missing), it won't appear as overdue even if previous instances were overdue. The comment says "this is the current instance date" which suggests the scheduled date represents the next/current occurrence, not a past overdue occurrence.

User's Scenario:

  1. User has a recurring task with scheduled date = yesterday
  2. User clicks "Today" in agenda view
  3. The task's scheduled date (yesterday) is checked by isOverdueTimeAware()
  4. The task is correctly identified as overdue
  5. BUT when the user updates the due date to today, the task still doesn't show because:
    • The overdue logic only checks scheduled for recurring tasks
    • If the user updated due but not scheduled, the task won't match either daily or overdue sections

Proposed Solutions

Approach: Improve the logic in getOverdueTasks() to handle recurring tasks more accurately.

Changes Required:

  • File: src/services/FilterService.ts:2584-2591

Implementation:

if (task.recurrence) {
  // For recurring tasks, check scheduled date (current instance)
  // Also check due date if it exists (user may set both)
  if (task.due) {
    if (isOverdueTimeAware(task.due, isCompleted, hideCompletedFromOverdue)) {
      return true;
    }
  }
  if (task.scheduled) {
    if (isOverdueTimeAware(task.scheduled, isCompleted, hideCompletedFromOverdue)) {
      return true;
    }
  }
  return false;
}

Pros:

  • Handles both due and scheduled dates for recurring tasks
  • Minimal code changes
  • Consistent with non-recurring task logic
  • Fixes the reported issue

Cons:

  • ⚠️ May show tasks as overdue that shouldn't be if both dates are set inconsistently
  • ⚠️ Need to verify this doesn't break existing recurring task behavior

Solution 2: Separate Overdue Logic for Recurring vs Non-Recurring

Approach: Create distinct overdue detection logic based on task type.

Changes Required:

  • File: src/services/FilterService.ts:2573-2617
  • New method: isRecurringTaskOverdue(task: TaskInfo): boolean

Implementation:

private isRecurringTaskOverdue(task: TaskInfo, isCompleted: boolean, hideCompletedFromOverdue: boolean): boolean {
  // For recurring tasks, we need to check if the current instance is overdue
  // The scheduled date represents the current/next occurrence

  // Check if there's an uncompleted instance in the past
  if (task.scheduled) {
    const scheduledDate = parseDateToUTC(task.scheduled);
    const today = parseDateToUTC(getTodayString());

    if (isBefore(scheduledDate, today) && !isCompleted) {
      return !hideCompletedFromOverdue || !isCompleted;
    }
  }

  // Also check due date if the recurring task has one set
  if (task.due) {
    return isOverdueTimeAware(task.due, isCompleted, hideCompletedFromOverdue);
  }

  return false;
}

// Then use it in getOverdueTasks:
if (task.recurrence) {
  return this.isRecurringTaskOverdue(task, isCompleted, hideCompletedFromOverdue);
}

Pros:

  • Clear separation of concerns
  • Easier to test and maintain
  • Can handle complex recurring task scenarios

Cons:

  • ⚠️ More code to maintain
  • ⚠️ Need thorough testing for edge cases
  • ⚠️ Requires understanding of recurring task lifecycle

Solution 3: Add Explicit Overdue Instance Tracking

Approach: Track overdue instances separately in the task metadata.

Changes Required:

  • File: src/types.ts - Add overdue_instances?: string[] field
  • File: src/services/FilterService.ts - Update recurring task logic
  • File: src/views/AgendaView.ts - Display overdue instances

Implementation:

// In types.ts
export interface TaskInfo {
  // ... existing fields
  complete_instances?: string[];
  overdue_instances?: string[]; // NEW: Track overdue instances
}

// In getOverdueTasks
if (task.recurrence) {
  // Check if current instance is overdue OR if there are tracked overdue instances
  const hasOverdueInstances = task.overdue_instances && task.overdue_instances.length > 0;

  if (hasOverdueInstances) {
    return true;
  }

  // Check current scheduled date
  if (task.scheduled) {
    return isOverdueTimeAware(task.scheduled, isCompleted, hideCompletedFromOverdue);
  }

  return false;
}

Pros:

  • Most accurate tracking of overdue recurring tasks
  • Maintains history of overdue instances
  • Future-proof for complex scenarios

Cons:

  • Requires schema changes
  • Need migration for existing tasks
  • More complex implementation
  • Higher risk of breaking existing functionality

Solution 1 is recommended for the following reasons:

  1. Minimal Risk: Only changes one small section of code
  2. Fixes the Reported Issue: Handles the user's exact scenario (recurring task with due date)
  3. Low Complexity: Easy to understand and maintain
  4. Quick to Implement: Can be done in a single PR
  5. Easy to Test: Can verify with the existing test file (once helper functions are fixed)

Next Steps

  1. Fix the test file - Update to use correct factory methods
  2. Verify test fails - Confirm the test reproduces the bug
  3. Implement Solution 1 - Make the code changes
  4. Verify test passes - Confirm the fix works
  5. Manual testing - Test in the actual plugin
  6. Edge case testing - Test various recurring task scenarios

Additional Notes

  • The codebase uses UTC anchors for date comparisons to avoid timezone issues
  • The hideCompletedFromOverdue setting (default: true) hides completed tasks from overdue section
  • Recurring tasks use the scheduled field to track the current instance date
  • The complete_instances array tracks which instances of a recurring task have been completed

The codebase follows these patterns for date handling:

  • Use parseDateToUTC() for internal logic and comparisons
  • Use parseDateToLocal() for UI display
  • Use isOverdueTimeAware(), isSameDateSafe(), isBeforeDateSafe() for safe date comparisons
  • Store dates in YYYY-MM-DD format using formatDateForStorage()