diff --git a/apps/web/src/app/(dashboard)/agents/_components/weekly-routine-grid.tsx b/apps/web/src/app/(dashboard)/agents/_components/weekly-routine-grid.tsx
index 060c2c0..5baab23 100644
--- a/apps/web/src/app/(dashboard)/agents/_components/weekly-routine-grid.tsx
+++ b/apps/web/src/app/(dashboard)/agents/_components/weekly-routine-grid.tsx
@@ -125,91 +125,112 @@ export const WeeklyRoutineGrid = () => {
const isToday = dayIndex === today;
return (
-
+ {/* Today label above card */}
+ {isToday ? (
+
+
+ Today ·{" "}
+ {new Date().toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })}
+
+
+ ) : (
+
)}
- >
- {/* Day header */}
+
- {day}
-
+ {/* Day header */}
+
+ {day}
+
- {/* Routine cards for this day */}
-
- {routinesByDay[dayIndex]?.map((routine) => {
- const colors = getColors(routine.color);
+ {/* Routine cards for this day */}
+
+ {routinesByDay[dayIndex]?.map((routine) => {
+ const colors = getColors(routine.color);
- return (
-
-
-
-
- {routine.title}
-
-
- {formatTime(
- routine.schedule.hour,
- routine.schedule.minute,
- )}
-
-
-
-
-
-
- {routine.title}
-
- {routine.description && (
-
- {routine.description}
+
+
+
+ {routine.title}
- )}
-
-
- Schedule
-
- {formatScheduleDays(routine.schedule.daysOfWeek)}
-
-
-
- Time
-
- {formatTime(
- routine.schedule.hour,
- routine.schedule.minute,
- )}
-
-
-
-
Priority
-
- {formatPriority(routine.priority)}
-
+
+ {formatTime(
+ routine.schedule.hour,
+ routine.schedule.minute,
+ )}
+
+
+
+
+
+
+ {routine.title}
+
+ {routine.description && (
+
+ {routine.description}
+
+ )}
+
+
+ Schedule
+
+ {formatScheduleDays(
+ routine.schedule.daysOfWeek,
+ )}
+
+
+
+ Time
+
+ {formatTime(
+ routine.schedule.hour,
+ routine.schedule.minute,
+ )}
+
+
+
+ Priority
+
+ {formatPriority(routine.priority)}
+
+
-
-
-
- );
- })}
+
+
+ );
+ })}
+
);
diff --git a/packages/backend/convex/routines.ts b/packages/backend/convex/routines.ts
index cac894e..8351810 100644
--- a/packages/backend/convex/routines.ts
+++ b/packages/backend/convex/routines.ts
@@ -116,6 +116,24 @@ export const trigger = mutation({
const now = Date.now();
+ // Deduplicate: skip if an active task with the same title already exists
+ const existingTasks = await ctx.db
+ .query("tasks")
+ .withIndex("by_createdAt")
+ .collect();
+ const activeStatuses = ["inbox", "assigned", "in_progress", "review"];
+ const duplicate = existingTasks.find(
+ (t) => t.title === routine.title && activeStatuses.includes(t.status),
+ );
+ if (duplicate) {
+ // Already an active task for this routine — skip creation
+ await ctx.db.patch(args.routineId, {
+ lastTriggeredAt: now,
+ updatedAt: now,
+ });
+ return duplicate._id;
+ }
+
// Create task from routine template
const taskId = await ctx.db.insert("tasks", {
title: routine.title,