feat(me): add 30-day mention age cutoff to filter stale @-mentions

Previously, query_mentioned_in returned mentions from any time in the
entity's history as long as the entity was still open (or recently closed).
This caused noise: a mention from 6 months ago on a still-open issue would
appear in the dashboard indefinitely.

Now the SQL filters notes by created_at > mention_cutoff_ms, defaulting to
30 days. The recency_cutoff (7 days) still governs closed/merged entity
visibility — this new cutoff governs mention note age on open entities.

Signature change: query_mentioned_in gains a mention_cutoff_ms parameter.
All existing test call sites updated. Two new tests verify the boundary:
- mentioned_in_excludes_old_mention_on_open_issue (45-day mention filtered)
- mentioned_in_includes_recent_mention_on_open_issue (5-day mention kept)
This commit is contained in:
teernisse
2026-03-12 10:07:07 -04:00
parent 7e5ffe35d3
commit 9c909df6b2
3 changed files with 67 additions and 16 deletions

View File

@@ -849,6 +849,7 @@ fn build_mentioned_in_sql(project_clause: &str) -> String {
LEFT JOIN note_ts_issue nt ON nt.issue_id = ci.id
WHERE n.is_system = 0
AND n.author_username != ?1
AND n.created_at > ?3
AND LOWER(n.body) LIKE '%@' || LOWER(?1) || '%'
UNION ALL
-- MR mentions (scoped to candidate entities only)
@@ -862,6 +863,7 @@ fn build_mentioned_in_sql(project_clause: &str) -> String {
LEFT JOIN note_ts_mr nt ON nt.merge_request_id = cm.id
WHERE n.is_system = 0
AND n.author_username != ?1
AND n.created_at > ?3
AND LOWER(n.body) LIKE '%@' || LOWER(?1) || '%'
ORDER BY 6 DESC
LIMIT 500",
@@ -871,7 +873,8 @@ fn build_mentioned_in_sql(project_clause: &str) -> String {
/// Query issues and MRs where the user is @mentioned but not assigned/authored/reviewing.
///
/// Includes open items unconditionally, plus recently-closed/merged items
/// (where `updated_at > recency_cutoff_ms`).
/// (where `updated_at > recency_cutoff_ms`). Only considers mentions in notes
/// created after `mention_cutoff_ms` (typically 30 days ago).
///
/// Returns deduplicated results sorted by attention priority then recency.
pub fn query_mentioned_in(
@@ -879,14 +882,16 @@ pub fn query_mentioned_in(
username: &str,
project_ids: &[i64],
recency_cutoff_ms: i64,
mention_cutoff_ms: i64,
) -> Result<Vec<MeMention>> {
let project_clause = build_project_clause_at("p.id", project_ids, 3);
let project_clause = build_project_clause_at("p.id", project_ids, 4);
// Materialized CTEs avoid pathological query plans for project-scoped mentions.
let sql = build_mentioned_in_sql(&project_clause);
let mut params: Vec<Box<dyn rusqlite::types::ToSql>> = Vec::new();
params.push(Box::new(username.to_string()));
params.push(Box::new(recency_cutoff_ms));
params.push(Box::new(mention_cutoff_ms));
for &pid in project_ids {
params.push(Box::new(pid));
}