feat(gamification): flag cascade-completed parents as auto_completed

complete_subtask_and_check_parent now sets auto_completed = 1 on the
parent when it closes via the cascade. The subtask UPDATE itself
remains at the default 0, so explicit user taps still count toward
the Phase 8 daily total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 20:13:28 +01:00
parent 729b82cf50
commit 92b32282d9

View File

@@ -451,7 +451,13 @@ pub async fn complete_subtask_and_check_parent(pool: &SqlitePool, subtask_id: &s
})?; })?;
if pending == 0 { if pending == 0 {
sqlx::query("UPDATE tasks SET done = 1, done_at = datetime('now') WHERE id = ?") // Phase 8: flag the cascade so the daily-count query can exclude
// it. The subtask UPDATE (above) stays at auto_completed = 0 — the
// user explicitly ticked it, so it counts.
sqlx::query(
"UPDATE tasks SET done = 1, done_at = datetime('now'), auto_completed = 1 \
WHERE id = ?",
)
.bind(&pid) .bind(&pid)
.execute(&mut *tx) .execute(&mut *tx)
.await .await
@@ -2016,4 +2022,59 @@ mod tests {
let missing = get_setting(&pool, "nonexistent").await.unwrap(); let missing = get_setting(&pool, "nonexistent").await.unwrap();
assert!(missing.is_none()); assert!(missing.is_none());
} }
#[tokio::test]
async fn cascade_sets_auto_completed_on_parent_only() {
let pool = test_pool().await;
// Parent + two subtasks.
insert_task(
&pool,
"parent",
"Parent task",
"inbox",
None,
None,
None,
None,
)
.await
.unwrap();
insert_subtask(&pool, "s1", "Step 1", "parent")
.await
.unwrap();
insert_subtask(&pool, "s2", "Step 2", "parent")
.await
.unwrap();
complete_subtask_and_check_parent(&pool, "s1")
.await
.unwrap();
complete_subtask_and_check_parent(&pool, "s2")
.await
.unwrap();
// Parent auto-closed.
let parent_auto: i64 =
sqlx::query_scalar("SELECT auto_completed FROM tasks WHERE id = 'parent'")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(
parent_auto, 1,
"parent closed by cascade must be auto_completed = 1"
);
// Subtasks themselves are manual.
let s1_auto: i64 = sqlx::query_scalar("SELECT auto_completed FROM tasks WHERE id = 's1'")
.fetch_one(&pool)
.await
.unwrap();
let s2_auto: i64 = sqlx::query_scalar("SELECT auto_completed FROM tasks WHERE id = 's2'")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(s1_auto, 0, "subtask completion is manual");
assert_eq!(s2_auto, 0, "subtask completion is manual");
}
} }