// Quiet per-task history counters. // // The app design calls for recovery/reporting features that can notice repeated // pushes, backlog moves, burnout skips, and locked-hour leakage. This file keeps // that history as immutable counters attached to each task. /// Internal counters used for future filtering, reporting, and scheduling hints. /// /// These counters answer "what has happened to this task over time?" without /// changing the primary task state. They are intentionally quiet metadata: daily /// task cards should not show all of this by default, but reports and filters can /// use it to identify patterns such as tasks repeatedly pushed, skipped during /// burnout, or completed inside locked hours. /// /// Like [Task], this is immutable. Increment helpers return a new /// [TaskStatistics] value so calling code can update a task through `copyWith` /// while retaining predictable before/after behavior. class TaskStatistics { const TaskStatistics({ this.skippedDuringBurnoutCount = 0, this.manuallyPushedCount = 0, this.autoPushedCount = 0, this.movedToBacklogCount = 0, this.restoredFromBacklogCount = 0, this.missedCount = 0, this.cancelledCount = 0, this.completedLateCount = 0, this.completedDuringLockedHoursCount = 0, this.completedDuringLockedHoursMinutes = 0, this.completedAfterShieldCount = 0, this.completedAfterPushCount = 0, this.totalPushesBeforeCompletion = 0, }); /// Number of times this task was skipped during Shield/recovery behavior. final int skippedDuringBurnoutCount; /// Number of times the user explicitly pushed this task. final int manuallyPushedCount; /// Number of times the scheduler moved this task to make room for something. final int autoPushedCount; /// Number of times this task was moved from schedule/today into backlog. final int movedToBacklogCount; /// Number of times this task came back from backlog into a planned slot. final int restoredFromBacklogCount; /// Number of times this task missed its intended timing. final int missedCount; /// Number of times this task was intentionally cancelled. final int cancelledCount; /// Number of times this task was completed after its scheduled window. final int completedLateCount; /// Number of completion events that overlapped locked hours. final int completedDuringLockedHoursCount; /// Total minutes completed while overlapping locked hours. final int completedDuringLockedHoursMinutes; /// Dormant V2-compatible counter for future Shield completion workflows. final int completedAfterShieldCount; /// Number of completions that happened after at least one push. final int completedAfterPushCount; /// Sum of push counts present when completion was recorded. final int totalPushesBeforeCompletion; /// Return a copy with selected counters changed. /// /// Counters default to their current values when omitted, which keeps small /// increment helpers concise and avoids direct mutation. TaskStatistics copyWith({ int? skippedDuringBurnoutCount, int? manuallyPushedCount, int? autoPushedCount, int? movedToBacklogCount, int? restoredFromBacklogCount, int? missedCount, int? cancelledCount, int? completedLateCount, int? completedDuringLockedHoursCount, int? completedDuringLockedHoursMinutes, int? completedAfterShieldCount, int? completedAfterPushCount, int? totalPushesBeforeCompletion, }) { return TaskStatistics( skippedDuringBurnoutCount: skippedDuringBurnoutCount ?? this.skippedDuringBurnoutCount, manuallyPushedCount: manuallyPushedCount ?? this.manuallyPushedCount, autoPushedCount: autoPushedCount ?? this.autoPushedCount, movedToBacklogCount: movedToBacklogCount ?? this.movedToBacklogCount, restoredFromBacklogCount: restoredFromBacklogCount ?? this.restoredFromBacklogCount, missedCount: missedCount ?? this.missedCount, cancelledCount: cancelledCount ?? this.cancelledCount, completedLateCount: completedLateCount ?? this.completedLateCount, completedDuringLockedHoursCount: completedDuringLockedHoursCount ?? this.completedDuringLockedHoursCount, completedDuringLockedHoursMinutes: completedDuringLockedHoursMinutes ?? this.completedDuringLockedHoursMinutes, completedAfterShieldCount: completedAfterShieldCount ?? this.completedAfterShieldCount, completedAfterPushCount: completedAfterPushCount ?? this.completedAfterPushCount, totalPushesBeforeCompletion: totalPushesBeforeCompletion ?? this.totalPushesBeforeCompletion, ); } /// Record that the task was removed from active planning and stored for later. TaskStatistics incrementMovedToBacklog() { return copyWith(movedToBacklogCount: movedToBacklogCount + 1); } /// Record a recovery/Shield skip. This is distinct from manual cancellation. TaskStatistics incrementSkippedDuringBurnout() { return copyWith(skippedDuringBurnoutCount: skippedDuringBurnoutCount + 1); } /// Record an explicit user push action. TaskStatistics incrementManualPush() { return copyWith(manuallyPushedCount: manuallyPushedCount + 1); } /// Record scheduler-driven movement caused by another placement. TaskStatistics incrementAutoPush() { return copyWith(autoPushedCount: autoPushedCount + 1); } /// Record that a backlog item was scheduled again. TaskStatistics incrementRestoredFromBacklog() { return copyWith(restoredFromBacklogCount: restoredFromBacklogCount + 1); } /// Record a missed intended time or required task handling event. TaskStatistics incrementMissed() { return copyWith(missedCount: missedCount + 1); } /// Record that the task was deliberately cancelled. TaskStatistics incrementCancelled() { return copyWith(cancelledCount: cancelledCount + 1); } /// Record that the task was completed after its planned end. TaskStatistics incrementCompletedLate() { return copyWith(completedLateCount: completedLateCount + 1); } /// Record completion that overlapped locked time by [minutes]. /// /// Both count and minutes are tracked because reports may want either "how /// often did this happen?" or "how much time leaked into locked blocks?" TaskStatistics incrementCompletedDuringLockedHours(int minutes) { return copyWith( completedDuringLockedHoursCount: completedDuringLockedHoursCount + 1, completedDuringLockedHoursMinutes: completedDuringLockedHoursMinutes + minutes, ); } /// Record that completion happened after [pushCount] prior pushes. TaskStatistics recordPushesBeforeCompletion(int pushCount) { if (pushCount <= 0) { return this; } return copyWith( completedAfterPushCount: completedAfterPushCount + 1, totalPushesBeforeCompletion: totalPushesBeforeCompletion + pushCount, ); } }