Files
ClaudeDo/docs/superpowers/specs/2026-06-03-daily-prep-design.md
2026-06-04 08:42:41 +02:00

8.9 KiB

Daily Prep ("Prime Claude") — Design

Date: 2026-06-03

Overview

Turn the existing Prime Time warm-up into a daily preparation ("Tagesvorbereitung"). At a scheduled time (or on demand), Claude reads the open tasks, estimates effort, and selects a focused subset into the MyDay list — capped so it never moves everything in. Claude does the reasoning itself (agentic), via the already-registered ClaudeDo MCP. This replaces the current "ping" behavior entirely.

A later phase will feed external tickets (Jira, possibly a second system) into the same candidate pool; that is out of scope for this spec.

Goals

  • Scheduled and manual ("Tag vorbereiten" button) daily prep.
  • Claude picks a subset of open tasks into MyDay, ordered so related tasks sit together.
  • Effort-aware selection, hard-capped at X open MyDay tasks.
  • Keep existing MyDay tasks across re-runs; only top up to X.
  • Candidates limited to tasks in repos that are not excluded from the weekly report.

Non-Goals

  • External ticket integration (Jira etc.) — future phase.
  • Group labels/headers in the MyDay view — grouping is ordering-only via SortOrder.
  • A user-editable prep prompt — the prompt is fixed, parameterized.

Key Decisions

Topic Decision
Who reasons Agentic — Claude decides via MCP tools.
MyDay model TaskEntity.IsMyDay flag (smart list smart:my-day).
Grouping Ordering only via existing SortOrder (no new field, no migration for grouping).
Selection Effort estimate, hard cap X tasks/day.
Candidates Status == Idle, BlockedByTaskId == null, list WorkingDir not under ReportExcludedPaths.
Re-run Keep existing MyDay tasks; top up to X.
Trigger Existing Prime schedule and a manual button.
Ping Removed — daily prep replaces it.
Prompt Fixed, with injected parameters (X, today's date).
Tool access Reuse the globally registered claudedo MCP — no separate --mcp-config.

Architecture

1. MCP tools (extend ExternalMcpService, port 47822)

The worker already exposes ExternalMcpService as the claudedo MCP server. Add two tools; they automatically surface as mcp__claudedo__get_daily_prep_candidates and mcp__claudedo__set_my_day.

  • get_daily_prep_candidates() → JSON containing:

    • candidates[]: open, non-blocked tasks in non-excluded repos, each with id, title, description, listName, isStarred, scheduledFor, age (age derived from CreatedAt).
    • currentMyDay[]: currently-IsMyDay open tasks (so Claude sees remaining capacity).
    • Filter: Status == Idle AND BlockedByTaskId == null AND the task's list WorkingDir does not start with any prefix in AppSettings.ReportExcludedPaths (default ["C:\\Private"]; case-insensitive prefix match, same semantics as the weekly report).
  • set_my_day(taskId, isMyDay, sortOrder?)

    • Sets IsMyDay and (optionally) SortOrder on the task via TaskRepository.
    • Broadcasts TaskUpdated via HubBroadcaster so the UI updates live.
    • Cap-guard: when isMyDay == true, count current open (Idle) tasks with IsMyDay == true. If count >= X, reject with an error message ("MyDay limit {X} reached"). isMyDay == false is always allowed. X = AppSettings.DailyPrepMaxTasks. This guarantees the "never move everything in" invariant server-side, independent of Claude's behavior.

2. DailyPrepRunner (replaces ping logic)

Rename IPrimeRunner/PrimeRunnerIDailyPrepRunner/DailyPrepRunner (the "ping" concept is gone). It:

  • Loads AppSettings (X = DailyPrepMaxTasks).
  • Builds the fixed prompt with injected parameters (X, today's date).
  • Invokes claude -p --output-format stream-json --verbose with:
    • --permission-mode set so the headless run won't block on permission prompts,
    • --allowedTools mcp__claudedo__get_daily_prep_candidates mcp__claudedo__set_my_day,
    • --max-turns 30 (constant), timeout 5 min (constant; larger than the old 60s ping).
  • No --mcp-config — relies on the globally registered claudedo MCP (the worker runs as the user via the per-user logon Scheduled Task, so the headless run inherits the user-scope registration and its auth).
  • Returns an outcome (e.g. number of tasks added) for broadcasting.

3. Scheduler

PrimeScheduler is unchanged in structure — it now calls IDailyPrepRunner instead of the ping runner. NextDueCalculator and the schedule model are untouched.

4. Manual trigger

  • Worker hub method RunDailyPrepNow() invokes the same DailyPrepRunner.
  • UI button "Tag vorbereiten" in the MyDay list header.
  • Single-flight guard: if a prep run is already in progress, the trigger reports "already running" and does not start a parallel run (applies to both schedule and button).

5. Parameter config

  • New field DailyPrepMaxTasks (int, default 5) on AppSettingsEntity.
  • Plumbing: EF config + migration, AppSettingsRepository, WorkerHub AppSettings DTO, UI DTO mirror + WorkerClient, and a numeric editor in the Prime Claude settings tab.
  • ReportExcludedPaths is reused as-is (already on AppSettings).

Data Flow

  1. Trigger (schedule due or button) → DailyPrepRunner.RunAsync.
  2. Runner loads AppSettings (X), builds prompt, launches Claude.
  3. Claude → get_daily_prep_candidates → DB query returns filtered candidates + current MyDay.
  4. Claude estimates effort, tops up to X total, calls set_my_day(id, true, sortOrder) for each chosen task (consecutive sortOrder for related tasks).
  5. ExternalMcpService writes IsMyDay/SortOrder, broadcasts TaskUpdated → MyDay list updates live.
  6. Runner updates LastRunAt, broadcasts "prep done" (count added).

Fixed Prompt (parameterized)

Content (parameters in {}):

Du bereitest meinen Arbeitstag für {today} vor.

  1. Rufe get_daily_prep_candidates auf.
  2. Behalte bereits als MyDay markierte offene Tasks.
  3. Fülle bis maximal {X} offene Tasks gesamt in MyDay auf — niemals mehr.
  4. Schätze pro Task grob den Aufwand; wähle eine machbare Mischung (nicht nur Großbrocken). Priorisiere isStarred, fällige (scheduledFor) und ältere Tasks.
  5. Lege thematisch verwandte Tasks durch aufeinanderfolgende sortOrder-Werte nebeneinander.
  6. Setze die Auswahl via set_my_day(id, true, sortOrder). Markiere nichts außerhalb der Kandidatenliste.

Injected parameters: {today} (date) and {X} (= DailyPrepMaxTasks).

Error Handling

  • No candidates → Claude marks nothing; runner reports "0 added".
  • Claude run fails / times out → log + failure broadcast (existing scheduler event channel); LastRunAt is set on attempt, as today, to avoid tight retry loops.
  • set_my_day on an invalid/ineligible id → tool returns an error string; Claude adapts.
  • Cap exceeded → tool returns an error; Claude stops adding.
  • Concurrent trigger → single-flight guard reports "already running".

Testing

Real SQLite + real git (project convention).

  • get_daily_prep_candidates: only Idle; blocked excluded; tasks in excluded repos (ReportExcludedPaths) excluded; current MyDay tasks included.
  • set_my_day: sets flag + SortOrder; broadcasts TaskUpdated; cap-guard rejects at limit; unset always allowed.
  • DailyPrepRunner: prompt contains {X} + date; args contain --allowedTools + permission-mode + --max-turns; success/failure outcomes via an IClaudeProcess fake.
  • Rename IPrimeRunnerIDailyPrepRunner requires syncing PrimeScheduler tests/fakes.

Files to Create / Modify (high level)

Data

  • Models/AppSettingsEntity.cs — add DailyPrepMaxTasks.
  • Configuration/AppSettingsEntityConfiguration.cs — map new column.
  • Migrations/ — new migration for daily_prep_max_tasks.
  • Repositories/AppSettingsRepository.cs — persist new field.

Worker

  • External/ExternalMcpService.cs — add get_daily_prep_candidates, set_my_day (+ cap-guard).
  • Prime/PrimeRunner.csDailyPrepRunner.cs; Prime/Interfaces/IPrimeRunner.csIDailyPrepRunner.cs; prompt builder + arg builder.
  • Prime/PrimeScheduler.cs — depend on IDailyPrepRunner.
  • Hub/WorkerHub.cs — AppSettings DTO field; RunDailyPrepNow().
  • Program.cs — DI registration update.

UI

  • Services/WorkerClient.cs + AppSettings DTO mirror — new field; RunDailyPrepNow call.
  • Prime Claude settings tab VM/view — numeric editor for DailyPrepMaxTasks.
  • MyDay list header — "Tag vorbereiten" button + command (Lists/IslandsShell VM).

Tests

  • ClaudeDo.Worker.Tests — MCP tools, runner, scheduler fakes.
  • ClaudeDo.Data.Tests — AppSettings persistence (if covered there).
  • ClaudeDo.Ui.Tests — settings VM / button wiring as applicable.

Future Phase (out of scope)

External ticket sources (Jira, possibly a second system) feed into the candidate pool used by get_daily_prep_candidates, behind a task-source abstraction. Designed separately.