# Plan — Worker log → footer + Log Visualizer overlay Design: `docs/superpowers/specs/2026-06-23-worker-log-footer-overlay-design.md`. Build on `main`, TDD, commit per task (Conventional Commits, explicit paths — shared worktree). Build `-c Release`. ## Task 1 — `LogRingBuffer` (Worker) + tests - `src/ClaudeDo.Worker/Logging/WorkerLogRecord.cs` — `record WorkerLogRecord(string Message, WorkerLogLevel Level, DateTime TimestampUtc)`. - `src/ClaudeDo.Worker/Logging/LogRingBuffer.cs` — thread-safe, `TimeSpan window` + int cap; `Append(record)`, `Snapshot()`. Uses an injected clock func (`Func`) for testability (default `() => DateTime.UtcNow`). - Tests: age eviction, cap eviction, snapshot order. **No `DateTime.UtcNow` in tests — drive the clock.** ## Task 2 — `BroadcastLogSink` (Worker) + tests - `src/ClaudeDo.Worker/Logging/BroadcastLogSink.cs : ILogEventSink` — level map, render (+exception first line), append-all-levels, broadcast Warn/Err via deferred `HubBroadcaster` (`Attach`), dedupe window (const 120s), loop-guard (skip SignalR `SourceContext` for broadcast; swallow broadcast exceptions). Inject clock func. - Broadcaster is an abstraction the test can fake: depend on a tiny `Func?` set by `Attach`, OR on `HubBroadcaster` directly (it's a sealed class — prefer a delegate to keep the test pure). Use a delegate. - Tests: all levels buffered; only Warn/Err invoke the broadcast delegate; dedupe suppresses 2nd identical within window but still buffers; exception rendering; SignalR-source event buffered but not broadcast. ## Task 3 — wire into `Program.cs` + `WorkerHub.GetRecentLogs` - `Program.cs`: create `LogRingBuffer` + `BroadcastLogSink` locals before build; `.WriteTo.Sink(broadcastSink)`; `AddSingleton(logBuffer)`; after build `broadcastSink.Attach((m,l,t) => broadcaster.WorkerLog(m,l,t))` using resolved `HubBroadcaster`. - `WorkerHub`: inject `LogRingBuffer`; `public IReadOnlyList GetRecentLogs()` → snapshot mapped to DTO. Add `WorkerLogRecordDto` (Hub or shared). Update `WorkerHub` ctor → check hub-construction call sites/tests. - Build Worker `-c Release`; run Worker.Tests (filtered to new + hub). ## Task 4 — `IWorkerClient.GetRecentLogsAsync` + WorkerClient + fakes - `IWorkerClient` + `WorkerClient` impl (`_hub.InvokeAsync>("GetRecentLogs", ct)`). - Update fakes: `tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs`, Worker.Tests UiVm fake(s) → return `Array.Empty()`. - Build Ui + Worker.Tests. ## Task 5 — `LogVisualizerViewModel` + View + dialog wiring + tests - VM (Modals/), View (Modals/, ModalShell), `IDialogService.ShowLogVisualizerAsync` + `WindowDialogService` impl. - `IslandsShellViewModel.OpenLogVisualizerCommand` (resolves VM, loads, shows). Make footer worker-log line a clickable Button → command. - Localization `vm.logVisualizer` en+de. - Tests: VM load/populate/filter. Build App `-c Release`; Ui.Tests + Localization.Tests. ## Task 6 — verify + docs - Full relevant test pass. Update `src/ClaudeDo.Ui/CLAUDE.md` (overlay VM/view, footer click) + `src/ClaudeDo.Worker/CLAUDE.md` (Logging/ folder, sink, GetRecentLogs, WorkerLog now carries Serilog Warn/Err). Note visual-verification gap (overlay render) for the user.