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).
This commit is contained in:
@@ -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.
|
- **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`
|
- **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)
|
- **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**
|
- **DailyNoteRepository**, **WeekReportRepository**, **TaskRunRepository**, **SubtaskRepository**, **AppSettingsRepository**
|
||||||
|
|
||||||
`TaskRepository.DeleteAsync` and `ListRepository.DeleteAsync` also delete the on-disk attachment
|
`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
|
- Worktrees: add (**serialized** to avoid a commondir race), remove, prune, list paths for branch
|
||||||
- Branches: current, list local, checkout, delete
|
- Branches: current, list local, checkout, delete
|
||||||
- Staging/commit: status porcelain, add-all, add-path, commit via stdin
|
- 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
|
- Merge: ff-only, no-ff, abort, mid-merge detection (`MERGE_HEAD`), conflicted files
|
||||||
- Revert: `RevertMergeCommitAsync` (`git revert --no-edit -m 1 <sha>`), `RevertAbortAsync`, `IsMidRevertAsync` (`REVERT_HEAD`, mirrors `IsMidMergeAsync`)
|
- Revert: `RevertMergeCommitAsync` (`git revert --no-edit -m 1 <sha>`), `RevertAbortAsync`, `IsMidRevertAsync` (`REVERT_HEAD`, mirrors `IsMidMergeAsync`)
|
||||||
- `PreviewMergeAsync` (non-destructive check via `git merge-tree --write-tree`), `CountChangedFilesAsync`, rev-parse, is-git-repo
|
- `PreviewMergeAsync` (non-destructive check via `git merge-tree --write-tree`), `CountChangedFilesAsync`, rev-parse, is-git-repo
|
||||||
|
|||||||
@@ -215,15 +215,6 @@ public sealed class GitService
|
|||||||
args.AddRange(paths);
|
args.AddRange(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> 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)
|
public async Task WorktreeRemoveAsync(string repoDir, string worktreePath, bool force = false, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var args = new List<string> { "worktree", "remove" };
|
var args = new List<string> { "worktree", "remove" };
|
||||||
|
|||||||
@@ -33,9 +33,4 @@ public sealed class SubtaskRepository
|
|||||||
{
|
{
|
||||||
await _context.Subtasks.Where(s => s.Id == subtaskId).ExecuteDeleteAsync(ct);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,4 @@ public sealed class TaskAttachmentRepository
|
|||||||
.ExecuteDeleteAsync(ct);
|
.ExecuteDeleteAsync(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAllForTaskAsync(string taskId, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
await _context.TaskAttachments
|
|
||||||
.Where(a => a.TaskId == taskId)
|
|
||||||
.ExecuteDeleteAsync(ct);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,10 +84,6 @@ public sealed class TaskRepository
|
|||||||
await _context.SaveChangesAsync(ct);
|
await _context.SaveChangesAsync(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kept for backwards-compatibility with callers using the old name.
|
|
||||||
public Task<List<TaskEntity>> GetByListAsync(string listId, CancellationToken ct = default)
|
|
||||||
=> GetByListIdAsync(listId, ct);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the ids of every descendant of <paramref name="taskId"/> (children, grandchildren, ...),
|
/// Returns the ids of every descendant of <paramref name="taskId"/> (children, grandchildren, ...),
|
||||||
/// walking the ParentTaskId chain breadth-first. Does not include the task itself.
|
/// 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);
|
.ExecuteUpdateAsync(s => s.SetProperty(t => t.SortOrder, (maxSort ?? -1) + 1), ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<TaskEntity>> GetByCreatorAsync(string createdBy, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
return await _context.Tasks
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(t => t.CreatedBy == createdBy)
|
|
||||||
.OrderByDescending(t => t.CreatedAt)
|
|
||||||
.ToListAsync(ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all tasks that qualify as "real" Idle backlog items for online mirroring:
|
/// Returns all tasks that qualify as "real" Idle backlog items for online mirroring:
|
||||||
/// Status==Idle, no parent, PlanningPhase==None, not blocked.
|
/// Status==Idle, no parent, PlanningPhase==None, not blocked.
|
||||||
|
|||||||
@@ -65,12 +65,4 @@ public sealed class WorktreeRepository
|
|||||||
{
|
{
|
||||||
return await _context.Worktrees.AsNoTracking().ToListAsync(ct);
|
return await _context.Worktrees.AsNoTracking().ToListAsync(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<WorktreeEntity>> GetByStatesAsync(
|
|
||||||
IReadOnlyCollection<WorktreeState> states, CancellationToken ct = default)
|
|
||||||
{
|
|
||||||
return await _context.Worktrees.AsNoTracking()
|
|
||||||
.Where(w => states.Contains(w.State))
|
|
||||||
.ToListAsync(ct);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,18 +312,6 @@ public class StreamLineFormatter
|
|||||||
return s.Length <= max ? s : s[..max] + "…";
|
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)
|
public static string Trim(string text)
|
||||||
{
|
{
|
||||||
if (text.Length <= MaxLength) return text;
|
if (text.Length <= MaxLength) return text;
|
||||||
|
|||||||
@@ -140,13 +140,6 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable
|
|||||||
_worker.TaskQuestionResolvedEvent += _onTaskQuestionResolved;
|
_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()
|
private void ClearPendingQuestion()
|
||||||
{
|
{
|
||||||
PendingQuestionId = null;
|
PendingQuestionId = null;
|
||||||
|
|||||||
@@ -100,10 +100,4 @@ public sealed partial class PrimeClaudeTabViewModel : ViewModelBase
|
|||||||
Rows.Remove(row);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
|
|||||||
@@ -86,19 +86,6 @@ public sealed class TaskAttachmentRepositoryTests : IDisposable
|
|||||||
Assert.Null(result);
|
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]
|
[Fact]
|
||||||
public async Task ListByTaskId_ordered_by_created_at()
|
public async Task ListByTaskId_ordered_by_created_at()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
|
||||||
<PackageReference Include="xunit" Version="2.5.3" />
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -91,48 +91,5 @@ public class StreamLineFormatterTests
|
|||||||
Assert.Null(_formatter.FormatLine(delta));
|
Assert.Null(_formatter.FormatLine(delta));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- FormatFile and Trim ---
|
// --- 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user