152 lines
7.7 KiB
Markdown
152 lines
7.7 KiB
Markdown
# 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`.
|
||
- `PrepFinished` → `IsPrepRunning=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 `PrepStarted` → `WorkerClient.PrepStartedEvent` →
|
||
`DetailsIslandViewModel` 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)` → `PrepFinished` → `IsPrepRunning=false`.
|
||
5. 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 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×`PrepLine` → `PrepFinished` (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.
|