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

7.7 KiB
Raw Blame History

Daily Prep — Live Output View + Clear Day — Design

Date: 2026-06-03

Overview

Two follow-ups to the daily-prep ("Prime Claude") feature:

  1. Live output view. While Claude prepares the day, there is no feedback. Add a live, human-readable view of the prep run's output, shown as a new content mode in the existing right-hand Details island (mirroring how Daily Notes works — a mode swap, not a separate window/column).
  2. Clear Day button. A MyDay-header button that clears the MyDay selection immediately.

Goals

  • See the prep run's progress live, rendered with the same friendly terminal renderer used for task runs (assistant text + tool calls like set_my_day …, not raw NDJSON).
  • Both manual (button) and scheduled prep runs stream into the log.
  • The manual button opens the prep view; a scheduled run fills the log silently and is opened via a dedicated "Vorbereitungs-Log" button (the existing PrimeStatus footer remains the hint that a run happened).
  • A "Tag leeren" button clears all MyDay tasks (any status) with no confirmation.

Non-Goals

  • No new island/column and no popup/overlay — reuse the Details island as a mode swap.
  • No persistence of prep output across app restarts (in-memory log only).
  • No undo for Clear Day (re-runnable via "Tag vorbereiten").

Key Decisions

Topic Decision
Rendering Reuse the existing SessionTerminalView / StreamLineFormatter renderer.
Location New IsPrepMode content panel inside the Details island (like IsNotesMode).
Lifecycle Manual click opens the view (UI-local); PrepStarted/PrepLine/PrepFinished events fill the log regardless of current mode; scheduled runs do not auto-open.
Open after schedule Dedicated "Vorbereitungs-Log" header button + existing PrimeStatus footer hint.
Clear Day scope All MyDay tasks regardless of status.
Clear Day confirm None — clear directly.

Architecture

Feature A — Live prep output

Worker

  • Extend IPrimeBroadcaster (src/ClaudeDo.Worker/Prime/Interfaces/IPrimeBroadcaster.cs) with PrepStartedAsync(), PrepLineAsync(string line), PrepFinishedAsync(bool success).
  • Implement in HubBroadcaster (src/ClaudeDo.Worker/Hub/HubBroadcaster.cs) sending SignalR events PrepStarted, PrepLine (string), PrepFinished (bool).
  • PrimeRunner (src/ClaudeDo.Worker/Prime/PrimeRunner.cs): inject IPrimeBroadcaster. In FireAsync, after the single-flight gate is entered and a run will actually happen: call PrepStartedAsync() before RunAsync; replace the discard lambda with async line => await _broadcaster.PrepLineAsync(line); call PrepFinishedAsync(result.IsSuccess) after. The "already running" early-return path emits nothing (no run occurs). Both scheduled and manual runs go through FireAsync, so both stream.

UI

  • WorkerClient (src/ClaudeDo.Ui/Services/WorkerClient.cs): register _hub.On<…>("PrepStarted"/"PrepLine"/"PrepFinished", …) each via Dispatcher.UIThread.Post, raising PrepStartedEvent / PrepLineEvent(string) / PrepFinishedEvent(bool). Declare these on IWorkerClient.
  • DetailsIslandViewModel: add IsPrepMode (bool), IsPrepRunning (bool), a dedicated PrepLog (ObservableCollection<LogLineViewModel>), and ShowPrep() (calls Bind(null), sets IsNotesMode=false, IsPrepMode=true). Subscribe to the three prep events in the ctor (always active, independent of mode):
    • PrepStarted → clear PrepLog, IsPrepRunning=true.
    • PrepLine → format the line with the same StreamLineFormatter path used by the stdout branch of OnTaskMessage, append a LogLineViewModel to PrepLog.
    • PrepFinishedIsPrepRunning=false (optionally append a status line). Mode exclusivity: the normal task-details panel becomes visible on !IsNotesMode && !IsPrepMode; ShowNotes() also sets IsPrepMode=false; Bind(task) resets both flags.
  • DetailsIslandView.axaml: add a third <Panel IsVisible="{Binding IsPrepMode}"> in the body grid alongside the existing details/notes panels, rendering PrepLog in the terminal style (reuse the LogLineViewModel item template used by SessionTerminalView).

Wiring

  • TasksIslandViewModel: add a PrepRequested event (mirror NotesRequested). PrepareDayCommand raises PrepRequested in addition to calling RunDailyPrepNowAsync(). Add ShowPrepLogCommand that raises PrepRequested. Add the "Vorbereitungs-Log" button to the MyDay header (IsVisible="{Binding IsMyDayList}").
  • IslandsShellViewModel: wire Tasks.PrepRequested += () => Details.ShowPrep().

Feature B — Clear Day

Worker

  • WorkerHub.ClearMyDay() (src/ClaudeDo.Worker/Hub/WorkerHub.cs): query ids where IsMyDay == true; ExecuteUpdateAsync setting is_my_day = false; broadcast TaskUpdated(id) for each affected id (the UI reloads the current list on TaskUpdated).

UI

  • IWorkerClient.ClearMyDayAsync() + WorkerClient impl invoking "ClearMyDay".
  • TasksIslandViewModel.ClearDayCommand calls _worker.ClearMyDayAsync() (no confirm). Add the "Tag leeren" button to the MyDay header next to "Tag vorbereiten".

Data Flow (live view)

  1. Trigger (schedule or button) → PrimeRunner.FireAsync.
  2. PrepStartedAsync() → SignalR PrepStartedWorkerClient.PrepStartedEventDetailsIslandViewModel clears PrepLog, sets IsPrepRunning.
  3. Each Claude stdout line → PrepLineAsync(line)PrepLine → formatted, appended to PrepLog (visible if the user is in prep mode; filled silently otherwise).
  4. Run ends → PrepFinishedAsync(success)PrepFinishedIsPrepRunning=false.
  5. Manual button click also raised PrepRequestedDetails.ShowPrep() (view open). After a scheduled run, the user clicks "Vorbereitungs-Log" to open it.

Error Handling

  • Prep run fails/times out → PrepFinished(false); the existing PrimeFired footer status still reports failure.
  • "Already running" → no prep events emitted (no run happened); existing behavior intact.
  • ClearMyDay with zero MyDay tasks → no-op, no broadcasts.

Testing

  • Worker: PrimeRunner streams PrepStarted → N×PrepLinePrepFinished (fake IClaudeProcess invokes onStdoutLine with sample lines; fake IPrimeBroadcaster records calls). WorkerHub.ClearMyDay clears all IsMyDay rows and broadcasts per id (real SQLite, mirror existing hub tests).
  • UI: DetailsIslandViewModel appends to PrepLog on PrepLineEvent and ShowPrep() sets the mode flags (mutual exclusivity with notes); TasksIslandViewModel.ClearDayCommand calls ClearMyDayAsync (stub worker client).

Files (high level)

Modify

  • src/ClaudeDo.Worker/Prime/Interfaces/IPrimeBroadcaster.cs
  • src/ClaudeDo.Worker/Hub/HubBroadcaster.cs
  • src/ClaudeDo.Worker/Prime/PrimeRunner.cs
  • src/ClaudeDo.Worker/Hub/WorkerHub.cs (ClearMyDay)
  • src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs
  • src/ClaudeDo.Ui/Services/WorkerClient.cs
  • src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs
  • src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml
  • src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs
  • src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml
  • src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs
  • src/ClaudeDo.Localization/locales/en.json, de.json (button labels)

Test

  • tests/ClaudeDo.Worker.Tests/Prime/PrimeRunnerTests.cs
  • tests/ClaudeDo.Worker.Tests/Hub/… (ClearMyDay)
  • tests/ClaudeDo.Ui.Tests/… (DetailsIslandViewModel prep events; TasksIslandViewModel ClearDay) + StubWorkerClient

Known fragility

Changing IWorkerClient / WorkerClient / VM constructors breaks hand-rolled fakes (StubWorkerClient, FakeWorkerClient) in both test projects — update all of them.