User preferences to dismiss todos upon merge or closure of associated Merge Request or Issue
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
Problem to solve
A To-Do raised automatically by a Merge Request being assigned, should be automatically marked 'done' by MR being merged/closed. This would avoid them accumulating.
Improving this is desirable for people using GitLab primarily as a repo & for merge requests (not as a task management system), to not find the Todo feature filled with outdated litter. If they ever do look at the Todos, finding it full of outdated items raised automatically but no longer relevant will be very effective in putting people off using this feature.
Proposed Solution: Two-Phase Approach
Phase 1: Prospective Auto-Resolution 🚀 HIGH IMPACT
Objective: Prevent new stale todos by auto-resolving them when MRs close/merge
Implementation:
- Hook into MR state change events (close/merge)
- Query for pending todos on that specific MR with stale-prone action types
-
Bulk update todos to 'done' with
resolved_by_action: :system_done
Code Changes:
# In MergeRequest state change callback or service
def auto_resolve_stale_todos_on_mr_completion
return unless merged? || closed?
stale_action_types = [
Todo::ASSIGNED, # 1
Todo::APPROVAL_REQUIRED, # 5
Todo::REVIEW_REQUESTED, # 9
Todo::ADDED_APPROVER # 13
]
Todo.where(
target: self,
state: :pending,
action: stale_action_types
).update_all(
state: :done,
resolved_by_action: Todo.resolved_by_actions[:system_done],
updated_at: Time.current
)
end
Impact: Eliminates 100% of future stale todos for these action types
Risk: Low - only affects todos that are definitively useless
Phase 2: Historical Data Cleanup 🧹 DEBT REDUCTION
Objective: Clean up the existing 6.3M stale todos to improve user experience immediately
Implementation:
class CleanupStaleTodosBackgroundMigration < Gitlab::BackgroundMigration::BatchedMigrationJob
STALE_ACTION_TYPES = [1, 5, 9, 13].freeze # assigned, approval_required, review_requested, added_approver
BATCH_SIZE = 1000
def perform
stale_todos = Todo.joins(
"JOIN merge_requests ON todos.target_id = merge_requests.id"
).where(
target_type: 'MergeRequest',
state: :pending,
action: STALE_ACTION_TYPES
).where(
"merge_requests.state IN ('merged', 'closed')"
).limit(BATCH_SIZE)
stale_todos.update_all(
state: :done,
resolved_by_action: Todo.resolved_by_actions[:system_done],
updated_at: Time.current
)
# Update user todo count caches
user_ids = stale_todos.distinct.pluck(:user_id)
Users::UpdateTodoCountCacheService.new(user_ids).execute
end
end
Rollout Strategy:
- Test on staging with a subset of data
- Gradual rollout - start with older stale todos (6+ months old)
- Monitor performance impact on database
- Measure user satisfaction - track todo dashboard engagement
Impact:
- Immediate UX improvement for 279,000+ affected users
- Reduces todo table size and improves query performance
- Clean slate for todo dashboard relevancy
Success Metrics
Phase 1 (Prospective):
- New stale todo creation: Should drop to 0 for target action types
- User todo completion rates: Should increase as todos become more actionable
Phase 2 (Historical):
- Stale todo reduction: 6.3M → 0 for historical data
- Todo dashboard engagement: Increased time spent on actual actionable todos
- User satisfaction scores: Improved ratings for todo functionality
Risk Mitigation
- Feature Flag: Phase 1 behind feature flag for easy rollback
- Monitoring: Track todo resolution rates and user complaints
- Granular Rollout: Start Phase 2 with oldest/safest data first
- Performance Testing: Ensure migration doesn't impact production queries
- User Communication: Changelog entry explaining todo cleanup