diff --git a/docs/superpowers/plans/2026-06-04-debug-logging-traceability.md b/docs/superpowers/plans/2026-06-04-debug-logging-traceability.md
new file mode 100644
index 0000000..5e716dd
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-04-debug-logging-traceability.md
@@ -0,0 +1,725 @@
+# Debug Logging & Frontend↔Backend Traceability Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build-configuration-driven logging — verbose in Debug builds (Rider run button), minimal `Warning`+ in Release (installed app) — with both processes writing one shared `claudedo-.log` and a `TaskId` correlation key threading UI→Worker→UI.
+
+**Architecture:** A new `ClaudeDo.Logging` library owns all Serilog setup: a `BuildConfig.IsDebug` runtime check (via the entry assembly's `DebuggableAttribute`, no `#if DEBUG`), a default-`TaskId` enricher, and a `LoggingSetup.Configure` method that branches sinks/levels on `IsDebug`. Worker and App both call it. `TaskId` rides Serilog `LogContext`, pushed at the per-task entry points on each side.
+
+**Tech Stack:** .NET 8, Serilog (core + File + Console sinks), Serilog.Extensions.Logging (App bridge), Serilog.AspNetCore (Worker, already present), xUnit.
+
+---
+
+### Task 1: Create the `ClaudeDo.Logging` project
+
+**Files:**
+- Create: `src/ClaudeDo.Logging/ClaudeDo.Logging.csproj`
+- Create: `src/ClaudeDo.Logging/Placeholder.cs` (temporary, removed in Task 2)
+- Modify: `ClaudeDo.slnx`
+
+- [ ] **Step 1: Create the csproj**
+
+Create `src/ClaudeDo.Logging/ClaudeDo.Logging.csproj`:
+
+```xml
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+> If NuGet reports a version conflict between `Serilog 4.1.0` and the `Serilog` core pulled transitively by `Serilog.AspNetCore 8.0.3` (Worker), align this `Serilog` version to whatever `Serilog.AspNetCore 8.0.3` resolves (check `dotnet list package --include-transitive`) and rebuild.
+
+- [ ] **Step 2: Add a temporary placeholder so the project compiles**
+
+Create `src/ClaudeDo.Logging/Placeholder.cs`:
+
+```csharp
+namespace ClaudeDo.Logging;
+
+internal static class Placeholder;
+```
+
+- [ ] **Step 3: Register the project in the solution**
+
+Edit `ClaudeDo.slnx` — add inside the `/src/` folder, after the `ClaudeDo.Localization` line:
+
+```xml
+
+```
+
+- [ ] **Step 4: Build the new project**
+
+Run: `dotnet build src/ClaudeDo.Logging/ClaudeDo.Logging.csproj -c Release`
+Expected: Build succeeded.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/ClaudeDo.Logging/ClaudeDo.Logging.csproj src/ClaudeDo.Logging/Placeholder.cs ClaudeDo.slnx
+git commit -m "build(logging): scaffold ClaudeDo.Logging project"
+```
+
+---
+
+### Task 2: `DefaultTaskIdEnricher` (TDD)
+
+Adds `TaskId = "-"` to any log event that doesn't already carry a `TaskId` property, so the `[{TaskId}]` column never renders the raw token. A pushed `LogContext` value takes precedence (because `Enrich.FromLogContext()` runs first and the property is then already present).
+
+**Files:**
+- Create: `src/ClaudeDo.Logging/DefaultTaskIdEnricher.cs`
+- Delete: `src/ClaudeDo.Logging/Placeholder.cs`
+- Create: `tests/ClaudeDo.Worker.Tests/Logging/DefaultTaskIdEnricherTests.cs`
+- Modify: `tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj` (add project reference)
+
+- [ ] **Step 1: Reference `ClaudeDo.Logging` from the test project**
+
+Edit `tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj` — add to the existing `ProjectReference` ItemGroup:
+
+```xml
+
+```
+
+- [ ] **Step 2: Write the failing test**
+
+Create `tests/ClaudeDo.Worker.Tests/Logging/DefaultTaskIdEnricherTests.cs`:
+
+```csharp
+using ClaudeDo.Logging;
+using Serilog;
+using Serilog.Context;
+using Serilog.Core;
+using Serilog.Events;
+
+namespace ClaudeDo.Worker.Tests.Logging;
+
+public sealed class DefaultTaskIdEnricherTests
+{
+ private sealed class CollectingSink : ILogEventSink
+ {
+ public List Events { get; } = new();
+ public void Emit(LogEvent logEvent) => Events.Add(logEvent);
+ }
+
+ [Fact]
+ public void AddsDash_WhenNoTaskIdInScope()
+ {
+ var sink = new CollectingSink();
+ using var logger = new LoggerConfiguration()
+ .Enrich.FromLogContext()
+ .Enrich.With(new DefaultTaskIdEnricher())
+ .WriteTo.Sink(sink)
+ .CreateLogger();
+
+ logger.Information("hello");
+
+ var prop = Assert.Single(sink.Events).Properties["TaskId"];
+ Assert.Equal("\"-\"", prop.ToString());
+ }
+
+ [Fact]
+ public void KeepsPushedTaskId_WhenInScope()
+ {
+ var sink = new CollectingSink();
+ using var logger = new LoggerConfiguration()
+ .Enrich.FromLogContext()
+ .Enrich.With(new DefaultTaskIdEnricher())
+ .WriteTo.Sink(sink)
+ .CreateLogger();
+
+ using (LogContext.PushProperty("TaskId", "task-42"))
+ logger.Information("hello");
+
+ var prop = Assert.Single(sink.Events).Properties["TaskId"];
+ Assert.Equal("\"task-42\"", prop.ToString());
+ }
+}
+```
+
+- [ ] **Step 3: Run the test to verify it fails**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter DefaultTaskIdEnricherTests`
+Expected: FAIL — `DefaultTaskIdEnricher` does not exist (compile error).
+
+- [ ] **Step 4: Implement the enricher and remove the placeholder**
+
+Delete `src/ClaudeDo.Logging/Placeholder.cs`.
+
+Create `src/ClaudeDo.Logging/DefaultTaskIdEnricher.cs`:
+
+```csharp
+using Serilog.Core;
+using Serilog.Events;
+
+namespace ClaudeDo.Logging;
+
+/// Ensures every log event carries a TaskId property (defaulting to "-")
+/// so the output template's [{TaskId}] column never renders the raw token.
+public sealed class DefaultTaskIdEnricher : ILogEventEnricher
+{
+ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ if (!logEvent.Properties.ContainsKey("TaskId"))
+ logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TaskId", "-"));
+ }
+}
+```
+
+- [ ] **Step 5: Run the test to verify it passes**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter DefaultTaskIdEnricherTests`
+Expected: PASS (2 tests).
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add src/ClaudeDo.Logging/DefaultTaskIdEnricher.cs tests/ClaudeDo.Worker.Tests/Logging/DefaultTaskIdEnricherTests.cs tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj
+git rm src/ClaudeDo.Logging/Placeholder.cs
+git commit -m "feat(logging): default TaskId enricher with passing tests"
+```
+
+---
+
+### Task 3: `BuildConfig.IsDebug`
+
+Detects whether the entry assembly was compiled in the Debug configuration (JIT optimizer disabled) — the runtime replacement for `#if DEBUG`.
+
+**Files:**
+- Create: `src/ClaudeDo.Logging/BuildConfig.cs`
+- Create: `tests/ClaudeDo.Worker.Tests/Logging/BuildConfigTests.cs`
+
+- [ ] **Step 1: Write the failing test**
+
+The test asserts the property returns *some* bool without throwing, and that the underlying detection logic agrees with the test assembly's own `DebuggableAttribute` (the test runs under whatever config `dotnet test` used). We assert the helper's result equals a locally-computed expectation so it passes under both Debug and Release test runs.
+
+Create `tests/ClaudeDo.Worker.Tests/Logging/BuildConfigTests.cs`:
+
+```csharp
+using System.Diagnostics;
+using System.Reflection;
+using ClaudeDo.Logging;
+
+namespace ClaudeDo.Worker.Tests.Logging;
+
+public sealed class BuildConfigTests
+{
+ [Fact]
+ public void IsDebug_MatchesEntryAssemblyDebuggableAttribute()
+ {
+ var entry = Assembly.GetEntryAssembly();
+ var expected = entry?
+ .GetCustomAttribute()
+ ?.IsJITOptimizerDisabled ?? false;
+
+ Assert.Equal(expected, BuildConfig.IsDebug);
+ }
+}
+```
+
+- [ ] **Step 2: Run the test to verify it fails**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter BuildConfigTests`
+Expected: FAIL — `BuildConfig` does not exist (compile error).
+
+- [ ] **Step 3: Implement `BuildConfig`**
+
+Create `src/ClaudeDo.Logging/BuildConfig.cs`:
+
+```csharp
+using System.Diagnostics;
+using System.Reflection;
+
+namespace ClaudeDo.Logging;
+
+/// Runtime build-configuration detection — the replacement for #if DEBUG.
+/// Debug builds compile with the JIT optimizer disabled; Release builds enable it.
+public static class BuildConfig
+{
+ public static bool IsDebug { get; } =
+ Assembly.GetEntryAssembly()
+ ?.GetCustomAttribute()
+ ?.IsJITOptimizerDisabled ?? false;
+}
+```
+
+- [ ] **Step 4: Run the test to verify it passes**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter BuildConfigTests`
+Expected: PASS.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/ClaudeDo.Logging/BuildConfig.cs tests/ClaudeDo.Worker.Tests/Logging/BuildConfigTests.cs
+git commit -m "feat(logging): runtime Debug-build detection via DebuggableAttribute"
+```
+
+---
+
+### Task 4: `LoggingSetup.Configure`
+
+The single shared configuration entry point. Applies enrichers, the output template, and branches sinks/levels on `BuildConfig.IsDebug`.
+
+**Files:**
+- Create: `src/ClaudeDo.Logging/LoggingSetup.cs`
+- Create: `tests/ClaudeDo.Worker.Tests/Logging/LoggingSetupTests.cs`
+
+- [ ] **Step 1: Write the failing test**
+
+Verifies a configured logger actually writes a `Warning` (emitted in both build configs) to a `claudedo-*.log` file under the given log root.
+
+Create `tests/ClaudeDo.Worker.Tests/Logging/LoggingSetupTests.cs`:
+
+```csharp
+using ClaudeDo.Logging;
+using Serilog;
+
+namespace ClaudeDo.Worker.Tests.Logging;
+
+public sealed class LoggingSetupTests
+{
+ [Fact]
+ public void Configure_WritesSharedLogFile()
+ {
+ var logRoot = Path.Combine(Path.GetTempPath(), "claudedo-logtest-" + Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(logRoot);
+ try
+ {
+ var logger = LoggingSetup.Configure(new LoggerConfiguration(), "test", logRoot).CreateLogger();
+ logger.Warning("marker-{Marker}", "xyz");
+ logger.Dispose(); // flush + release the file handle
+
+ var files = Directory.GetFiles(logRoot, "claudedo-*.log");
+ var file = Assert.Single(files);
+ var contents = File.ReadAllText(file);
+ Assert.Contains("marker-", contents);
+ Assert.Contains("test/", contents); // {Process} tag in the template
+ }
+ finally
+ {
+ try { Directory.Delete(logRoot, recursive: true); } catch { /* best effort */ }
+ }
+ }
+}
+```
+
+- [ ] **Step 2: Run the test to verify it fails**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter LoggingSetupTests`
+Expected: FAIL — `LoggingSetup` does not exist (compile error).
+
+- [ ] **Step 3: Implement `LoggingSetup`**
+
+Create `src/ClaudeDo.Logging/LoggingSetup.cs`:
+
+```csharp
+using Serilog;
+using Serilog.Events;
+
+namespace ClaudeDo.Logging;
+
+public static class LoggingSetup
+{
+ private const string OutputTemplate =
+ "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Process}/{SourceContext} [{TaskId}] {Message:lj}{NewLine}{Exception}";
+
+ /// Apply the shared ClaudeDo logging configuration.
+ /// Debug builds: Debug level, console + shared file. Release builds: Warning level, shared file only.
+ /// "worker" or "app" — tags every line so the interleaved file is readable.
+ /// Directory for the shared claudedo-.log (created if missing).
+ public static LoggerConfiguration Configure(LoggerConfiguration cfg, string processTag, string logRoot)
+ {
+ Directory.CreateDirectory(logRoot);
+ var logFile = Path.Combine(logRoot, "claudedo-.log");
+
+ cfg.Enrich.FromLogContext()
+ .Enrich.WithProperty("Process", processTag)
+ .Enrich.With(new DefaultTaskIdEnricher());
+
+ if (BuildConfig.IsDebug)
+ {
+ cfg.MinimumLevel.Debug()
+ .WriteTo.Console(outputTemplate: OutputTemplate)
+ .WriteTo.File(
+ logFile,
+ rollingInterval: RollingInterval.Day,
+ retainedFileCountLimit: 2,
+ shared: true,
+ outputTemplate: OutputTemplate);
+ }
+ else
+ {
+ cfg.MinimumLevel.Warning()
+ .WriteTo.File(
+ logFile,
+ rollingInterval: RollingInterval.Day,
+ retainedFileCountLimit: 2,
+ shared: true,
+ outputTemplate: OutputTemplate);
+ }
+
+ return cfg;
+ }
+}
+```
+
+- [ ] **Step 4: Run the test to verify it passes**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter LoggingSetupTests`
+Expected: PASS.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/ClaudeDo.Logging/LoggingSetup.cs tests/ClaudeDo.Worker.Tests/Logging/LoggingSetupTests.cs
+git commit -m "feat(logging): shared LoggingSetup with build-config sink branching"
+```
+
+---
+
+### Task 5: Wire the Worker to the shared setup
+
+**Files:**
+- Modify: `src/ClaudeDo.Worker/ClaudeDo.Worker.csproj`
+- Modify: `src/ClaudeDo.Worker/Program.cs:34-40`
+
+- [ ] **Step 1: Add the project reference**
+
+Edit `src/ClaudeDo.Worker/ClaudeDo.Worker.csproj` — add to the existing `ProjectReference` ItemGroup (the one with `ClaudeDo.Data`):
+
+```xml
+
+```
+
+- [ ] **Step 2: Replace the inline Serilog config**
+
+In `src/ClaudeDo.Worker/Program.cs`, replace lines 34-40:
+
+```csharp
+builder.Host.UseSerilog((ctx, lc) => lc
+ .MinimumLevel.Information()
+ .WriteTo.File(
+ System.IO.Path.Combine(logRoot, "worker-.log"),
+ rollingInterval: RollingInterval.Day,
+ retainedFileCountLimit: 7,
+ shared: true));
+```
+
+with:
+
+```csharp
+builder.Host.UseSerilog((ctx, lc) =>
+ ClaudeDo.Logging.LoggingSetup.Configure(lc, "worker", logRoot));
+```
+
+- [ ] **Step 3: Build the Worker**
+
+Run: `dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release`
+Expected: Build succeeded. (If the Worker is running and locks the Debug output, this Release build is unaffected.)
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add src/ClaudeDo.Worker/ClaudeDo.Worker.csproj src/ClaudeDo.Worker/Program.cs
+git commit -m "feat(logging): route Worker logging through shared LoggingSetup"
+```
+
+---
+
+### Task 6: Wire the App/Ui (currently log-silent) to the shared setup
+
+The App uses a plain `ServiceCollection` with **no** logging registered. Add the Serilog→`ILogger` bridge so all `ILogger` injections across App/Ui flow to the shared sinks, and flush on shutdown.
+
+**Files:**
+- Modify: `src/ClaudeDo.App/ClaudeDo.App.csproj`
+- Modify: `src/ClaudeDo.App/Program.cs`
+
+- [ ] **Step 1: Add packages and the project reference**
+
+Edit `src/ClaudeDo.App/ClaudeDo.App.csproj` — add to the package `ItemGroup`:
+
+```xml
+
+```
+
+and to the `ProjectReference` ItemGroup:
+
+```xml
+
+```
+
+- [ ] **Step 2: Add the logging registration in `BuildServices`**
+
+In `src/ClaudeDo.App/Program.cs`, inside `BuildServices()`, immediately after the `var sc = new ServiceCollection();` line (currently line 78), insert:
+
+```csharp
+ var logRoot = Path.Combine(Path.GetDirectoryName(dbPath)!, "logs");
+ var serilogLogger = ClaudeDo.Logging.LoggingSetup
+ .Configure(new Serilog.LoggerConfiguration(), "app", logRoot)
+ .CreateLogger();
+ sc.AddLogging(b => b.AddSerilog(serilogLogger, dispose: true));
+```
+
+Add these usings to the top of `Program.cs` (the `AddSerilog` `ILoggingBuilder` extension lives in the `Serilog` namespace; `AddLogging` lives in `Microsoft.Extensions.DependencyInjection`, already imported):
+
+```csharp
+using Serilog;
+using Microsoft.Extensions.Logging;
+```
+
+> `dbPath` is already computed just above (`var dbPath = Paths.Expand(settings.DbPath);`). Its parent directory is `~/.todo-app`, so `logs` sits beside the Worker's log root.
+
+- [ ] **Step 3: Build the App**
+
+Run: `dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release`
+Expected: Build succeeded (pulls in Ui + Data + Logging).
+
+- [ ] **Step 4: Verify manually from Rider (visual-verification gap)**
+
+This is a Debug-build behavior that cannot be asserted in a Release test run. Launch the App from Rider's run button and confirm:
+- A `claudedo-*.log` appears in `~/.todo-app/logs/`.
+- Console output (Rider run window) shows `Debug`-level lines tagged `app/...`.
+
+Flag to the user that this step needs their eyes.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/ClaudeDo.App/ClaudeDo.App.csproj src/ClaudeDo.App/Program.cs
+git commit -m "feat(logging): wire App/Ui logging to shared LoggingSetup"
+```
+
+---
+
+### Task 7: Push `TaskId` into `LogContext` in the Worker
+
+Wraps the two per-task entry points so every nested log line (runner, state service, worktree, planning) carries the task's id automatically.
+
+**Files:**
+- Modify: `src/ClaudeDo.Worker/Runner/TaskRunner.cs:47` (`RunAsync`) and `:171` (`ContinueAsync`)
+
+- [ ] **Step 1: Add the using directive**
+
+In `src/ClaudeDo.Worker/Runner/TaskRunner.cs`, add to the top usings:
+
+```csharp
+using Serilog.Context;
+```
+
+- [ ] **Step 2: Push TaskId at the top of `RunAsync`**
+
+In `RunAsync` (line 47), insert as the very first statement of the method body (before `string? mcpToken = null;`):
+
+```csharp
+ using var _taskScope = LogContext.PushProperty("TaskId", task.Id);
+```
+
+- [ ] **Step 3: Push TaskId at the top of `ContinueAsync`**
+
+In `ContinueAsync` (line 171), insert as the very first statement of the method body (before `TaskEntity task;`). The parameter is `taskId`:
+
+```csharp
+ using var _taskScope = LogContext.PushProperty("TaskId", taskId);
+```
+
+- [ ] **Step 4: Build the Worker**
+
+Run: `dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release`
+Expected: Build succeeded.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/ClaudeDo.Worker/Runner/TaskRunner.cs
+git commit -m "feat(logging): tag Worker task execution with TaskId for traceability"
+```
+
+---
+
+### Task 8: Push `TaskId` and add trace lines on the App side
+
+`WorkerClient` currently logs nothing. Inject `ILogger`, add a small helper that pushes `TaskId` + emits a `Debug` trace line, and route the fire-and-forget task-targeted hub calls through it. This produces the UI half of the UI→Worker→UI trace under a shared `TaskId`.
+
+**Files:**
+- Modify: `src/ClaudeDo.Ui/Services/WorkerClient.cs`
+- Modify: `src/ClaudeDo.App/Program.cs:101` (registration)
+
+- [ ] **Step 1: Add usings and the logger field/ctor param**
+
+In `src/ClaudeDo.Ui/Services/WorkerClient.cs`, add to the usings:
+
+```csharp
+using Microsoft.Extensions.Logging;
+using Serilog.Context;
+```
+
+Add a field beside `private readonly HubConnection _hub;` (line 32):
+
+```csharp
+ private readonly ILogger _logger;
+```
+
+Change the constructor signature (line 68) from:
+
+```csharp
+ public WorkerClient(string signalRUrl)
+ {
+```
+
+to:
+
+```csharp
+ public WorkerClient(string signalRUrl, ILogger logger)
+ {
+ _logger = logger;
+```
+
+- [ ] **Step 2: Add the task-scoped invoke helper**
+
+In `src/ClaudeDo.Ui/Services/WorkerClient.cs`, add this private method next to `TryInvokeAsync` (after line 241):
+
+```csharp
+ /// Invoke a task-targeted hub method under a TaskId log scope, emitting a debug trace line.
+ private async Task InvokeForTaskAsync(string taskId, string method, params object?[] args)
+ {
+ using (LogContext.PushProperty("TaskId", taskId))
+ {
+ _logger.LogDebug("UI invoking {Method} for task {TaskId}", method, taskId);
+ await _hub.InvokeCoreAsync(method, args);
+ }
+ }
+```
+
+- [ ] **Step 3: Route the fire-and-forget task actions through the helper**
+
+In the same file, replace each of these method bodies:
+
+`RunNowAsync` (line 243):
+```csharp
+ public Task RunNowAsync(string taskId)
+ => InvokeForTaskAsync(taskId, "RunNow", taskId);
+```
+
+`ContinueTaskAsync` (line 248):
+```csharp
+ public Task ContinueTaskAsync(string taskId, string followUpPrompt)
+ => InvokeForTaskAsync(taskId, "ContinueTask", taskId, followUpPrompt);
+```
+
+`ResetTaskAsync` (line 253):
+```csharp
+ public Task ResetTaskAsync(string taskId)
+ => InvokeForTaskAsync(taskId, "ResetTask", taskId);
+```
+
+`CancelTaskAsync` (line 267):
+```csharp
+ public Task CancelTaskAsync(string taskId)
+ => InvokeForTaskAsync(taskId, "CancelTask", taskId);
+```
+
+`ApproveReviewAsync` (line 389):
+```csharp
+ public Task ApproveReviewAsync(string taskId)
+ => InvokeForTaskAsync(taskId, "ApproveReview", taskId);
+```
+
+`RejectReviewToQueueAsync` (line 394):
+```csharp
+ public Task RejectReviewToQueueAsync(string taskId, string feedback)
+ => InvokeForTaskAsync(taskId, "RejectReviewToQueue", taskId, feedback);
+```
+
+`RejectReviewToIdleAsync` (line 399):
+```csharp
+ public Task RejectReviewToIdleAsync(string taskId)
+ => InvokeForTaskAsync(taskId, "RejectReviewToIdle", taskId);
+```
+
+`CancelReviewAsync` (line 404):
+```csharp
+ public Task CancelReviewAsync(string taskId)
+ => InvokeForTaskAsync(taskId, "CancelReview", taskId);
+```
+
+> These all previously did `await _hub.InvokeAsync(method, ...)` with no return value, so converting them to expression-bodied delegations preserves behavior. Do **not** touch methods that return DTOs (e.g. `MergeTaskAsync`) or the planning methods — keep this change scoped to the void task actions above.
+
+- [ ] **Step 4: Update the DI registration to pass the logger**
+
+In `src/ClaudeDo.App/Program.cs`, replace line 101:
+
+```csharp
+ sc.AddSingleton(sp => new WorkerClient(sp.GetRequiredService().SignalRUrl));
+```
+
+with:
+
+```csharp
+ sc.AddSingleton(sp => new WorkerClient(
+ sp.GetRequiredService().SignalRUrl,
+ sp.GetRequiredService>()));
+```
+
+Add `using Microsoft.Extensions.Logging;` to the top of `Program.cs` if not already present.
+
+- [ ] **Step 5: Build the App**
+
+Run: `dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release`
+Expected: Build succeeded.
+
+> Note: `WorkerClient` is faked in tests via the `IWorkerClient` *interface* (hand-rolled fakes implement the interface, they do not subclass `WorkerClient`). This change adds a ctor parameter to the concrete class only and does not alter `IWorkerClient`, so the fakes are unaffected. Confirm by building the test projects in the next step.
+
+- [ ] **Step 6: Build the test projects to confirm fakes still compile**
+
+Run: `dotnet build tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release && dotnet build tests/ClaudeDo.Ui.Tests/ClaudeDo.Ui.Tests.csproj -c Release`
+Expected: Build succeeded for both.
+
+- [ ] **Step 7: Run the full Worker.Tests suite**
+
+Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release`
+Expected: PASS (all existing tests + the 4 new logging tests).
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add src/ClaudeDo.Ui/Services/WorkerClient.cs src/ClaudeDo.App/Program.cs
+git commit -m "feat(logging): tag UI task actions with TaskId + debug trace lines"
+```
+
+---
+
+## Final verification
+
+- [ ] **Build the whole desktop + worker stack in Release:**
+
+```bash
+dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release
+dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release
+```
+
+- [ ] **Run the logging tests:**
+
+```bash
+dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter "FullyQualifiedName~Logging"
+```
+Expected: PASS (DefaultTaskIdEnricher × 2, BuildConfig × 1, LoggingSetup × 1).
+
+- [ ] **Manual smoke test (visual-verification gap — needs the user):**
+ 1. Run the Worker and App from Rider (Debug build). Confirm both write to one `~/.todo-app/logs/claudedo-*.log` with `app/...` and `worker/...` lines.
+ 2. Run a task; grep that file for the task's id — confirm UI (`UI invoking RunNow…`) and Worker lines share the same `[]`.
+ 3. Build/install the Release app; confirm the log is near-silent (no `Debug`/`Information` noise, `Warning`+ only) and no console window logging.