From 81994aadcacf28a0e856c16932a0aa0d74eb6d7e Mon Sep 17 00:00:00 2001 From: mika kuns Date: Wed, 26 Aug 2026 10:11:46 +0200 Subject: [PATCH] refactor: delete unreachable members and redundant package refs Eight public members had no caller anywhere in src: GitService.GetFileDiffAsync, SubtaskRepository.DeleteByTaskIdAsync, TaskRepository.GetByListAsync (a backwards-compat alias for GetByListIdAsync) and .GetByCreatorAsync, WorktreeRepository.GetByStatesAsync, TaskMonitorViewModel.SetPendingQuestion (a duplicate of the live-event lambda), PrimeClaudeTabViewModel.ApplyFiredEvent, and StreamLineFormatter.FormatFile. TaskAttachmentRepository.DeleteAllForTaskAsync was reachable only from its own test; the ON DELETE CASCADE on task_attachments already covers it. Tests for the deleted members go with them. Dropped two package refs the platform already provides: EntityFrameworkCore.Design in Worker (the design-time factory and the migrations live in Data, which has its own ref) and System.IO.FileSystem.AccessControl in Installer.Tests (net8.0-windows ships the ACL APIs in the shared framework). --- src/ClaudeDo.Data/CLAUDE.md | 4 +- src/ClaudeDo.Data/Git/GitService.cs | 9 ---- .../Repositories/SubtaskRepository.cs | 5 --- .../Repositories/TaskAttachmentRepository.cs | 6 --- .../Repositories/TaskRepository.cs | 13 ------ .../Repositories/WorktreeRepository.cs | 8 ---- .../Helpers/StreamLineFormatter.cs | 12 ----- .../Islands/TaskMonitorViewModel.cs | 7 --- .../Settings/PrimeClaudeTabViewModel.cs | 6 --- src/ClaudeDo.Worker/ClaudeDo.Worker.csproj | 4 -- .../TaskAttachmentRepositoryTests.cs | 13 ------ .../ClaudeDo.Installer.Tests.csproj | 1 - .../Helpers/StreamLineFormatterTests.cs | 45 +------------------ 13 files changed, 3 insertions(+), 130 deletions(-) diff --git a/src/ClaudeDo.Data/CLAUDE.md b/src/ClaudeDo.Data/CLAUDE.md index 1cafe53e..f48f3a76 100644 --- a/src/ClaudeDo.Data/CLAUDE.md +++ b/src/ClaudeDo.Data/CLAUDE.md @@ -53,7 +53,7 @@ Worker's `QueuePicker` (`FromSqlRaw`), **not** here. - **TaskRepository** — CRUD, planning helpers (`CreateChildAsync`, `SetPlanningStartedAsync`, `DiscardPlanningAsync`, `UpdateChildAsync`), `UpdateAgentSettingsAsync`. ⚠️ Status-mutation primitives (`MarkRunningAsync`/`MarkDoneAsync`/`MarkFailedAsync`/`FlipAllRunningToFailedAsync`) are **`internal`** — only the Worker's `TaskStateService` may call them. `CreateChildAsync` produces children with `Status=Idle, PlanningPhase=None`. **Task-number allocation:** `AddAsync` (line 20) and `CreateChildAsync` (line 276) are the two sole task-creation paths; both call `TaskNumberAllocator.AddWithNumberAsync` to allocate a number atomically during the same transaction as the insert. Every other creation path (UI, MCP `add_task`, `batch_add_tasks`, Online Inbox sync, merge-helper handler tasks, planning children) routes through one of these two. - **ListRepository** — CRUD, `GetConfigAsync`/`SetConfigAsync` (upsert)/`DeleteConfigAsync` for `list_config` - **WorktreeRepository** — CRUD, `UpdateHeadAsync`, `SetStateAsync`, `SetMergedAsync` (atomically sets State=Merged **and** stamps MergeCommit in one update — the only writer of MergeCommit) -- **TaskAttachmentRepository** — `AddAsync`, `UpdateAsync`, `GetAsync(taskId, fileName)`, `ListByTaskIdAsync`, `DeleteAsync(taskId, fileName)`, `DeleteAllForTaskAsync` +- **TaskAttachmentRepository** — `AddAsync`, `UpdateAsync`, `GetAsync(taskId, fileName)`, `ListByTaskIdAsync`, `DeleteAsync(taskId, fileName)`. Bulk delete-per-task is the `ON DELETE CASCADE` on `task_attachments`, not a repository method. - **DailyNoteRepository**, **WeekReportRepository**, **TaskRunRepository**, **SubtaskRepository**, **AppSettingsRepository** `TaskRepository.DeleteAsync` and `ListRepository.DeleteAsync` also delete the on-disk attachment @@ -74,7 +74,7 @@ dir(s) via an optional `AttachmentStore` ctor param (defaults to the production - Worktrees: add (**serialized** to avoid a commondir race), remove, prune, list paths for branch - Branches: current, list local, checkout, delete - Staging/commit: status porcelain, add-all, add-path, commit via stdin -- Diffs: working tree, branch vs base, commit range `base..head` (shows a merged task's diff after the worktree is gone), per-file, diff-stat, committed files, has-changes +- Diffs: working tree, branch vs base, commit range `base..head` (shows a merged task's diff after the worktree is gone), diff-stat, committed files, has-changes - Merge: ff-only, no-ff, abort, mid-merge detection (`MERGE_HEAD`), conflicted files - Revert: `RevertMergeCommitAsync` (`git revert --no-edit -m 1 `), `RevertAbortAsync`, `IsMidRevertAsync` (`REVERT_HEAD`, mirrors `IsMidMergeAsync`) - `PreviewMergeAsync` (non-destructive check via `git merge-tree --write-tree`), `CountChangedFilesAsync`, rev-parse, is-git-repo diff --git a/src/ClaudeDo.Data/Git/GitService.cs b/src/ClaudeDo.Data/Git/GitService.cs index ac0a44a4..aa10b335 100644 --- a/src/ClaudeDo.Data/Git/GitService.cs +++ b/src/ClaudeDo.Data/Git/GitService.cs @@ -215,15 +215,6 @@ public sealed class GitService args.AddRange(paths); } - public async Task GetFileDiffAsync(string worktreePath, string? baseCommit, string relativePath, CancellationToken ct = default) - { - string[] args = string.IsNullOrEmpty(baseCommit) - ? ["diff", "--", relativePath] - : ["diff", $"{baseCommit}..HEAD", "--", relativePath]; - var (_, stdout, _) = await RunGitAsync(worktreePath, args, ct); - return stdout; - } - public async Task WorktreeRemoveAsync(string repoDir, string worktreePath, bool force = false, CancellationToken ct = default) { var args = new List { "worktree", "remove" }; diff --git a/src/ClaudeDo.Data/Repositories/SubtaskRepository.cs b/src/ClaudeDo.Data/Repositories/SubtaskRepository.cs index 53697010..fadf31dd 100644 --- a/src/ClaudeDo.Data/Repositories/SubtaskRepository.cs +++ b/src/ClaudeDo.Data/Repositories/SubtaskRepository.cs @@ -33,9 +33,4 @@ public sealed class SubtaskRepository { await _context.Subtasks.Where(s => s.Id == subtaskId).ExecuteDeleteAsync(ct); } - - public async Task DeleteByTaskIdAsync(string taskId, CancellationToken ct = default) - { - await _context.Subtasks.Where(s => s.TaskId == taskId).ExecuteDeleteAsync(ct); - } } diff --git a/src/ClaudeDo.Data/Repositories/TaskAttachmentRepository.cs b/src/ClaudeDo.Data/Repositories/TaskAttachmentRepository.cs index 28c83818..08d5cb58 100644 --- a/src/ClaudeDo.Data/Repositories/TaskAttachmentRepository.cs +++ b/src/ClaudeDo.Data/Repositories/TaskAttachmentRepository.cs @@ -42,10 +42,4 @@ public sealed class TaskAttachmentRepository .ExecuteDeleteAsync(ct); } - public async Task DeleteAllForTaskAsync(string taskId, CancellationToken ct = default) - { - await _context.TaskAttachments - .Where(a => a.TaskId == taskId) - .ExecuteDeleteAsync(ct); - } } diff --git a/src/ClaudeDo.Data/Repositories/TaskRepository.cs b/src/ClaudeDo.Data/Repositories/TaskRepository.cs index e70b3b83..894778fc 100644 --- a/src/ClaudeDo.Data/Repositories/TaskRepository.cs +++ b/src/ClaudeDo.Data/Repositories/TaskRepository.cs @@ -84,10 +84,6 @@ public sealed class TaskRepository await _context.SaveChangesAsync(ct); } - // Kept for backwards-compatibility with callers using the old name. - public Task> GetByListAsync(string listId, CancellationToken ct = default) - => GetByListIdAsync(listId, ct); - /// /// Returns the ids of every descendant of (children, grandchildren, ...), /// walking the ParentTaskId chain breadth-first. Does not include the task itself. @@ -142,15 +138,6 @@ public sealed class TaskRepository .ExecuteUpdateAsync(s => s.SetProperty(t => t.SortOrder, (maxSort ?? -1) + 1), ct); } - public async Task> GetByCreatorAsync(string createdBy, CancellationToken ct = default) - { - return await _context.Tasks - .AsNoTracking() - .Where(t => t.CreatedBy == createdBy) - .OrderByDescending(t => t.CreatedAt) - .ToListAsync(ct); - } - /// /// Returns all tasks that qualify as "real" Idle backlog items for online mirroring: /// Status==Idle, no parent, PlanningPhase==None, not blocked. diff --git a/src/ClaudeDo.Data/Repositories/WorktreeRepository.cs b/src/ClaudeDo.Data/Repositories/WorktreeRepository.cs index e5264932..2abf7c72 100644 --- a/src/ClaudeDo.Data/Repositories/WorktreeRepository.cs +++ b/src/ClaudeDo.Data/Repositories/WorktreeRepository.cs @@ -65,12 +65,4 @@ public sealed class WorktreeRepository { return await _context.Worktrees.AsNoTracking().ToListAsync(ct); } - - public async Task> GetByStatesAsync( - IReadOnlyCollection states, CancellationToken ct = default) - { - return await _context.Worktrees.AsNoTracking() - .Where(w => states.Contains(w.State)) - .ToListAsync(ct); - } } diff --git a/src/ClaudeDo.Ui/Helpers/StreamLineFormatter.cs b/src/ClaudeDo.Ui/Helpers/StreamLineFormatter.cs index eac49303..2a21f8de 100644 --- a/src/ClaudeDo.Ui/Helpers/StreamLineFormatter.cs +++ b/src/ClaudeDo.Ui/Helpers/StreamLineFormatter.cs @@ -312,18 +312,6 @@ public class StreamLineFormatter return s.Length <= max ? s : s[..max] + "…"; } - public string FormatFile(string filePath) - { - var sb = new StringBuilder(); - foreach (var line in File.ReadLines(filePath)) - { - var formatted = FormatLine(line); - if (formatted is not null) - sb.Append(formatted); - } - return Trim(sb.ToString()); - } - public static string Trim(string text) { if (text.Length <= MaxLength) return text; diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs index 1cdf4637..a7d809f2 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs @@ -140,13 +140,6 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable _worker.TaskQuestionResolvedEvent += _onTaskQuestionResolved; } - // Surface a pending question (used by live event + re-attach hydration). - public void SetPendingQuestion(string questionId, string question) - { - PendingQuestionId = questionId; - PendingQuestion = question; - } - private void ClearPendingQuestion() { PendingQuestionId = null; diff --git a/src/ClaudeDo.Ui/ViewModels/Modals/Settings/PrimeClaudeTabViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Modals/Settings/PrimeClaudeTabViewModel.cs index fd0f00a9..1f08d20e 100644 --- a/src/ClaudeDo.Ui/ViewModels/Modals/Settings/PrimeClaudeTabViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Modals/Settings/PrimeClaudeTabViewModel.cs @@ -100,10 +100,4 @@ public sealed partial class PrimeClaudeTabViewModel : ViewModelBase Rows.Remove(row); } - public void ApplyFiredEvent(PrimeFiredEvent evt) - { - var row = Rows.FirstOrDefault(r => r.Id == evt.ScheduleId); - if (row is null) return; - if (evt.Success) row.LastRunAt = evt.FiredAt; - } } diff --git a/src/ClaudeDo.Worker/ClaudeDo.Worker.csproj b/src/ClaudeDo.Worker/ClaudeDo.Worker.csproj index 15cd6b7d..9c7497d6 100644 --- a/src/ClaudeDo.Worker/ClaudeDo.Worker.csproj +++ b/src/ClaudeDo.Worker/ClaudeDo.Worker.csproj @@ -5,10 +5,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/tests/ClaudeDo.Data.Tests/TaskAttachmentRepositoryTests.cs b/tests/ClaudeDo.Data.Tests/TaskAttachmentRepositoryTests.cs index 8e73fbe0..951fd8af 100644 --- a/tests/ClaudeDo.Data.Tests/TaskAttachmentRepositoryTests.cs +++ b/tests/ClaudeDo.Data.Tests/TaskAttachmentRepositoryTests.cs @@ -86,19 +86,6 @@ public sealed class TaskAttachmentRepositoryTests : IDisposable Assert.Null(result); } - [Fact] - public async Task DeleteAllForTask_clears_all_rows_for_task() - { - await _repo.AddAsync(MakeAttachment("a.txt")); - await _repo.AddAsync(MakeAttachment("b.txt")); - await _repo.AddAsync(MakeAttachment("c.txt")); - - await _repo.DeleteAllForTaskAsync(TaskId); - - var list = await _repo.ListByTaskIdAsync(TaskId); - Assert.Empty(list); - } - [Fact] public async Task ListByTaskId_ordered_by_created_at() { diff --git a/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj b/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj index aeb3d207..9a9b4eb2 100644 --- a/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj +++ b/tests/ClaudeDo.Installer.Tests/ClaudeDo.Installer.Tests.csproj @@ -14,7 +14,6 @@ - diff --git a/tests/ClaudeDo.Ui.Tests/Helpers/StreamLineFormatterTests.cs b/tests/ClaudeDo.Ui.Tests/Helpers/StreamLineFormatterTests.cs index 9718d9a2..c04d8ac3 100644 --- a/tests/ClaudeDo.Ui.Tests/Helpers/StreamLineFormatterTests.cs +++ b/tests/ClaudeDo.Ui.Tests/Helpers/StreamLineFormatterTests.cs @@ -91,48 +91,5 @@ public class StreamLineFormatterTests Assert.Null(_formatter.FormatLine(delta)); } - // --- FormatFile and Trim --- - - [Fact] - public void FormatFile_ParsesAllLinesAndReturnsFormattedText() - { - var lines = new[] - { - """{"type":"assistant","message":{"content":[{"type":"text","text":"Hello"}]}}""", - """{"type":"assistant","message":{"content":[{"type":"tool_use","id":"x","name":"Bash","input":{"command":"ls"}}]}}""", - """{"type":"result","result":"Done."}""", - }; - var file = Path.GetTempFileName(); - try - { - File.WriteAllLines(file, lines); - var result = _formatter.FormatFile(file); - Assert.Contains("Hello", result); - Assert.Contains("[Bash]", result); - Assert.Contains("Done.", result); - } - finally - { - File.Delete(file); - } - } - - [Fact] - public void FormatFile_TrimsLargeContent() - { - var chunk = new string('x', 1000); - var line = "{\"type\":\"assistant\",\"message\":{\"content\":[{\"type\":\"text\",\"text\":\"" + chunk + "\"}]}}"; - var lines = Enumerable.Repeat(line, 65).ToArray(); - var file = Path.GetTempFileName(); - try - { - File.WriteAllLines(file, lines); - var result = _formatter.FormatFile(file); - Assert.True(result.Length <= 50_200, $"Expected <= 50200 but got {result.Length}"); - } - finally - { - File.Delete(file); - } - } + // --- Trim --- }