Files
ClaudeDo/docs/superpowers/specs/2026-06-02-prime-recurring-weekdays-design.md
2026-06-02 15:12:04 +02:00

5.0 KiB
Raw Permalink Blame History

Prime: recurring weekday schedule

Date: 2026-06-02 Status: Approved

Problem

The Prime feature fires a single non-interactive "ping" prompt to warm up the Claude usage window. Today a schedule is defined by a date range (StartDate/EndDate) plus a TimeOfDay and a single WorkdaysOnly toggle. This is awkward for the real use case: the user wants a recurring morning ping on specific weekdays, not a bounded calendar window.

Desired behavior: pick the days of the week (e.g. MonFri) and a time. The schedule recurs forever. Whenever the worker is running and it is one of the selected days, the ping fires at (or shortly after) the chosen time. Concretely: the worker autostarts on login, detects it is an eligible day around the target time, and fires the ping.

Decisions

  • Catch-up window: unchanged. Keep the existing 30-minute catch-up — if the worker boots within 30 min after the target time, the ping fires immediately; otherwise it waits for the next eligible day. (User chose "keep current 30 min".)
  • Day picker UI: seven compact toggle buttons in one row (Mo Tu We Th Fr Sa Su), highlighted when selected — not labeled checkboxes.

Design

1. Data model

PrimeScheduleEntity (ClaudeDo.Data/Models):

  • Remove: StartDate, EndDate, WorkdaysOnly
  • Add: Days — a [Flags] enum PrimeDays (Monday=1, Tuesday=2, Wednesday=4, Thursday=8, Friday=16, Saturday=32, Sunday=64), stored as a single days_of_week INTEGER column.
  • Keep: TimeOfDay, Enabled, LastRunAt, PromptOverride, CreatedAt.

Rationale for a bitmask over a CSV string or 7 bool columns: one column, trivial EF mapping (int), and a clean eligibility check.

PrimeScheduleEntityConfiguration: drop the start_date/end_date/ workdays_only property mappings; map Days to days_of_week (int, required, default 31 = MonFri).

2. Scheduling logic — NextDueCalculator

  • Drop all StartDate/EndDate gating (the EndDate < today early-out, the StartDate > today clamps, and the bounds check in IsEligibleDay).
  • IsEligibleDay(s, d) becomes: does s.Days contain the flag for d.DayOfWeek? (Map System.DayOfWeekPrimeDays.)
  • The existing forward search (loops up to 8 days ahead) now simply walks to the next selected weekday.
  • alreadyFiredToday (compares LastRunAt's local date to today) is unchanged.
  • The 30-min catch-up (FireImmediately) is unchanged.
  • A schedule with Days == 0 (none selected) is never eligible. UI validation prevents saving that state.

3. UI — SettingsModalView.axaml + PrimeScheduleRowViewModel

Row template changes:

  • Remove the ThemedDatePicker (range) and the single "MonFri" checkbox.
  • Add a horizontal row of 7 ToggleButtons (Mo Tu We Th Fr Sa Su), styled to highlight when checked, bound to seven bool properties on the row VM.
  • Keep the enabled checkbox, the time TextBox, the last-run label, and the remove button.

PrimeScheduleRowViewModel:

  • Replace StartDate/EndDate/WorkdaysOnly with seven [ObservableProperty] bools: MondaySunday.
  • Constructor decomposes dto.Days into the seven bools.
  • ToDto() composes the seven bools back into the Days int.

PrimeClaudeTabViewModel:

  • AddSchedule default: MonFri selected, time 07:00, enabled.
  • Validate: replace the StartDate > EndDate check with "at least one day must be selected"; keep the time-range (00:0023:59) check.

Update the explainer TextBlock text to describe weekday recurrence (keep the "fires immediately if started within 30 minutes of the target time" note).

4. Migration

New EF Core migration in ClaudeDo.Data/Migrations:

  • Add days_of_week INTEGER NOT NULL DEFAULT 31.
  • Backfill from existing rows: workdays_only = 131 (MonFri), workdays_only = 0127 (all 7 days).
  • Drop start_date, end_date, workdays_only.
  • Update the model snapshot.

5. DTOs

Both copies of PrimeScheduleDto (Worker ClaudeDo.Worker.Prime and UI ClaudeDo.Ui.Services) are passed over SignalR and must stay structurally compatible. In both: remove StartDate, EndDate, WorkdaysOnly; add a single int Days field (serializes cleanly as JSON; avoids sharing the enum across projects). PrimeScheduler.ToDto maps entity.Days(int).

PrimeScheduleRepository: update UpsertAsync (copy Days instead of the three removed fields) and ListAsync ordering (order by TimeOfDay instead of StartDate).

6. Tests

  • NextDueCalculatorTests — rewrite cases around weekday sets (e.g. MonFri skips weekend; single-day schedule; catch-up still fires; already-fired-today skips to next eligible day).
  • PrimeSchedulerTests — update fixture DTOs to the new shape.
  • PrimeScheduleRepositoryTests — update entity construction and assertions.
  • PrimeClaudeTabViewModelTests — update for the day-bool VM and new validation.

Out of scope

  • Per-schedule catch-up tuning (rejected; fixed 30 min).
  • Multiple times per day, timezones, or holiday calendars.