From 10f9fcd271bbac4ff26f32880db212b5bd3b7f00 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Thu, 3 Sep 2026 00:40:47 +0000 Subject: Show one row per recurring chore in the flat task list The recurrence scheduler materializes a series' next instance once the current one comes due, regardless of whether it was completed -- that's deliberate, so ignoring an occurrence doesn't stall the series. The side effect is that the flat task list shows an unfinished chore and its already-generated successor at the same time: "Bills" appears twice right now (Sep 1 overdue + Oct 1), as does "Bones's meds". CollapseRecurringSeries keeps only the earliest-due open instance per series, which is the one actually owed. Applied to the atoms list only -- the timeline and widget are date-anchored, where each instance correctly belongs to its own day, so they keep all of them. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B --- internal/handlers/atoms.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'internal/handlers') diff --git a/internal/handlers/atoms.go b/internal/handlers/atoms.go index 16cd003..131a3d0 100644 --- a/internal/handlers/atoms.go +++ b/internal/handlers/atoms.go @@ -27,6 +27,11 @@ func BuildUnifiedAtomList(s *store.Store, claudomator api.ClaudomatorClient) ([] if err != nil { log.Printf("Warning: failed to fetch native tasks: %v", err) } + // This is the flat "what's open" list, so a recurring chore belongs in it + // once, not once per already-materialized future instance -- see + // CollapseRecurringSeries. The timeline and widget deliberately don't do + // this: there, each instance sits on its own day. + nativeTasks = models.CollapseRecurringSeries(nativeTasks) atoms := make([]models.Atom, 0, len(gTasks)+len(nativeTasks)) -- cgit v1.2.3