3.3 KiB
3.3 KiB
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<DateTime>) for testability (default() => DateTime.UtcNow).- Tests: age eviction, cap eviction, snapshot order. No
DateTime.UtcNowin 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 deferredHubBroadcaster(Attach), dedupe window (const 120s), loop-guard (skip SignalRSourceContextfor broadcast; swallow broadcast exceptions). Inject clock func.- Broadcaster is an abstraction the test can fake: depend on a tiny
Func<string,WorkerLogLevel,DateTime,Task>?set byAttach, OR onHubBroadcasterdirectly (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: createLogRingBuffer+BroadcastLogSinklocals before build;.WriteTo.Sink(broadcastSink);AddSingleton(logBuffer); after buildbroadcastSink.Attach((m,l,t) => broadcaster.WorkerLog(m,l,t))using resolvedHubBroadcaster.WorkerHub: injectLogRingBuffer;public IReadOnlyList<WorkerLogRecordDto> GetRecentLogs()→ snapshot mapped to DTO. AddWorkerLogRecordDto(Hub or shared). UpdateWorkerHubctor → check hub-construction call sites/tests.- Build Worker
-c Release; run Worker.Tests (filtered to new + hub).
Task 4 — IWorkerClient.GetRecentLogsAsync + WorkerClient + fakes
IWorkerClient+WorkerClientimpl (_hub.InvokeAsync<List<WorkerLogEntry>>("GetRecentLogs", ct)).- Update fakes:
tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs, Worker.Tests UiVm fake(s) → returnArray.Empty<WorkerLogEntry>(). - Build Ui + Worker.Tests.
Task 5 — LogVisualizerViewModel + View + dialog wiring + tests
- VM (Modals/), View (Modals/, ModalShell),
IDialogService.ShowLogVisualizerAsync+WindowDialogServiceimpl. IslandsShellViewModel.OpenLogVisualizerCommand(resolves VM, loads, shows). Make footer worker-log line a clickable Button → command.- Localization
vm.logVisualizeren+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.