7.7 KiB
7.7 KiB
Daily Prep — Live Output View + Clear Day — Design
Date: 2026-06-03
Overview
Two follow-ups to the daily-prep ("Prime Claude") feature:
- 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).
- 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
PrimeStatusfooter 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) withPrepStartedAsync(),PrepLineAsync(string line),PrepFinishedAsync(bool success). - Implement in
HubBroadcaster(src/ClaudeDo.Worker/Hub/HubBroadcaster.cs) sending SignalR eventsPrepStarted,PrepLine(string),PrepFinished(bool). PrimeRunner(src/ClaudeDo.Worker/Prime/PrimeRunner.cs): injectIPrimeBroadcaster. InFireAsync, after the single-flight gate is entered and a run will actually happen: callPrepStartedAsync()beforeRunAsync; replace the discard lambda withasync line => await _broadcaster.PrepLineAsync(line); callPrepFinishedAsync(result.IsSuccess)after. The "already running" early-return path emits nothing (no run occurs). Both scheduled and manual runs go throughFireAsync, so both stream.
UI
WorkerClient(src/ClaudeDo.Ui/Services/WorkerClient.cs): register_hub.On<…>("PrepStarted"/"PrepLine"/"PrepFinished", …)each viaDispatcher.UIThread.Post, raisingPrepStartedEvent/PrepLineEvent(string)/PrepFinishedEvent(bool). Declare these onIWorkerClient.DetailsIslandViewModel: addIsPrepMode(bool),IsPrepRunning(bool), a dedicatedPrepLog(ObservableCollection<LogLineViewModel>), andShowPrep()(callsBind(null), setsIsNotesMode=false,IsPrepMode=true). Subscribe to the three prep events in the ctor (always active, independent of mode):PrepStarted→ clearPrepLog,IsPrepRunning=true.PrepLine→ format the line with the sameStreamLineFormatterpath used by the stdout branch ofOnTaskMessage, append aLogLineViewModeltoPrepLog.PrepFinished→IsPrepRunning=false(optionally append a status line). Mode exclusivity: the normal task-details panel becomes visible on!IsNotesMode && !IsPrepMode;ShowNotes()also setsIsPrepMode=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, renderingPrepLogin the terminal style (reuse theLogLineViewModelitem template used bySessionTerminalView).
Wiring
TasksIslandViewModel: add aPrepRequestedevent (mirrorNotesRequested).PrepareDayCommandraisesPrepRequestedin addition to callingRunDailyPrepNowAsync(). AddShowPrepLogCommandthat raisesPrepRequested. Add the "Vorbereitungs-Log" button to the MyDay header (IsVisible="{Binding IsMyDayList}").IslandsShellViewModel: wireTasks.PrepRequested += () => Details.ShowPrep().
Feature B — Clear Day
Worker
WorkerHub.ClearMyDay()(src/ClaudeDo.Worker/Hub/WorkerHub.cs): query ids whereIsMyDay == true;ExecuteUpdateAsyncsettingis_my_day = false; broadcastTaskUpdated(id)for each affected id (the UI reloads the current list onTaskUpdated).
UI
IWorkerClient.ClearMyDayAsync()+WorkerClientimpl invoking"ClearMyDay".TasksIslandViewModel.ClearDayCommandcalls_worker.ClearMyDayAsync()(no confirm). Add the "Tag leeren" button to the MyDay header next to "Tag vorbereiten".
Data Flow (live view)
- Trigger (schedule or button) →
PrimeRunner.FireAsync. PrepStartedAsync()→ SignalRPrepStarted→WorkerClient.PrepStartedEvent→DetailsIslandViewModelclearsPrepLog, setsIsPrepRunning.- Each Claude stdout line →
PrepLineAsync(line)→PrepLine→ formatted, appended toPrepLog(visible if the user is in prep mode; filled silently otherwise). - Run ends →
PrepFinishedAsync(success)→PrepFinished→IsPrepRunning=false. - Manual button click also raised
PrepRequested→Details.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 existingPrimeFiredfooter status still reports failure. - "Already running" → no prep events emitted (no run happened); existing behavior intact.
ClearMyDaywith zero MyDay tasks → no-op, no broadcasts.
Testing
- Worker:
PrimeRunnerstreamsPrepStarted→ N×PrepLine→PrepFinished(fakeIClaudeProcessinvokesonStdoutLinewith sample lines; fakeIPrimeBroadcasterrecords calls).WorkerHub.ClearMyDayclears all IsMyDay rows and broadcasts per id (real SQLite, mirror existing hub tests). - UI:
DetailsIslandViewModelappends toPrepLogonPrepLineEventandShowPrep()sets the mode flags (mutual exclusivity with notes);TasksIslandViewModel.ClearDayCommandcallsClearMyDayAsync(stub worker client).
Files (high level)
Modify
src/ClaudeDo.Worker/Prime/Interfaces/IPrimeBroadcaster.cssrc/ClaudeDo.Worker/Hub/HubBroadcaster.cssrc/ClaudeDo.Worker/Prime/PrimeRunner.cssrc/ClaudeDo.Worker/Hub/WorkerHub.cs(ClearMyDay)src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cssrc/ClaudeDo.Ui/Services/WorkerClient.cssrc/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cssrc/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axamlsrc/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cssrc/ClaudeDo.Ui/Views/Islands/TasksIslandView.axamlsrc/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cssrc/ClaudeDo.Localization/locales/en.json,de.json(button labels)
Test
tests/ClaudeDo.Worker.Tests/Prime/PrimeRunnerTests.cstests/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.