▍ humdrum codex / custard v0.3.0
license AGPL-3.0

feat(issues): sort within groups by status-change time

b989124027f9d5f9d7aaef337bfce2b3a8d0198e
humdrum <me@humdrum.me> · 2026-06-18 09:20

parent 0355245c

feat(issues): sort within groups by status-change time

Done shows newest completion on top (desc by updated_date); in progress / paused
/ backlog show oldest-entered on top (asc), so the longest-running item leads.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

1 files changed

web/templates/view.go +21 −1
@@ -178,9 +178,29 @@ 		return statuses[i] < statuses[j]
 	})
 	groups := make([]IssueGroup, 0, len(statuses))
 	for _, s := range statuses {
-		groups = append(groups, IssueGroup{Status: s, Tasks: byStatus[s]})
+		ts := byStatus[s]
+		// Done: newest completion on top. Active groups: oldest-entered on top.
+		desc := StatusKind(s) == "done"
+		sort.SliceStable(ts, func(a, b int) bool {
+			ka, kb := taskTime(ts[a]), taskTime(ts[b])
+			if desc {
+				return ka > kb
+			}
+			return ka < kb
+		})
+		groups = append(groups, IssueGroup{Status: s, Tasks: ts})
 	}
 	return groups
+}
+
+// taskTime is a task's sort key — its last status-change time (updated_date),
+// falling back to created_date. Stored as "YYYY-MM-DD HH:MM", so a lexical
+// compare is chronological.
+func taskTime(t backlog.Task) string {
+	if t.Updated != "" {
+		return t.Updated
+	}
+	return t.Created
 }
 
 // statusRank orders status buckets: in progress → paused → backlog → done → other.