fix(claude-do): merge fix(ui,worker): Diff-Zähler locale-fest machen und toten Par
ClaudeDo-Task: 1085277d-ba7e-4e58-b06d-751e1b99b96f
This commit is contained in:
@@ -1503,12 +1503,17 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
catch { /* best-effort */ }
|
||||
}
|
||||
|
||||
// Shared by TaskRowViewModel.UpdateFromEntity — this is the one parser for
|
||||
// `git diff --stat`'s summary line ("N insertions(+), M deletions(-)"). Anchored on the
|
||||
// literal "(+)"/"(-)" markers rather than the English words: git for Windows localizes
|
||||
// "insertion"/"deletion" to the OS language (e.g. German "Einfügung(en)"/"Löschung(en)"),
|
||||
// but always keeps the untranslated (+)/(-) suffix, so this reads correctly in any locale.
|
||||
internal static (int Additions, int Deletions) ParseDiffStat(string? stat)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stat)) return (0, 0);
|
||||
int add = 0, del = 0;
|
||||
var m1 = System.Text.RegularExpressions.Regex.Match(stat, @"(\d+)\s+insertion");
|
||||
var m2 = System.Text.RegularExpressions.Regex.Match(stat, @"(\d+)\s+deletion");
|
||||
var m1 = System.Text.RegularExpressions.Regex.Match(stat, @"(\d+)[^,\n]*\(\+\)");
|
||||
var m2 = System.Text.RegularExpressions.Regex.Match(stat, @"(\d+)[^,\n]*\(-\)");
|
||||
if (m1.Success) int.TryParse(m1.Groups[1].Value, out add);
|
||||
if (m2.Success) int.TryParse(m2.Groups[1].Value, out del);
|
||||
return (add, del);
|
||||
|
||||
@@ -500,7 +500,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
|
||||
// Any entity-backed refresh reflects a settled state, so the transient creation-phase
|
||||
// banner (set from the OperationProgress broadcast) has nothing left to announce.
|
||||
CreationPhase = null;
|
||||
var (add, del) = ParseDiffStat(t.Worktree?.DiffStat);
|
||||
var (add, del) = DetailsIslandViewModel.ParseDiffStat(t.Worktree?.DiffStat);
|
||||
Number = t.Number;
|
||||
Title = t.Title;
|
||||
ListName = t.List?.Name ?? "";
|
||||
@@ -529,18 +529,4 @@ public sealed partial class TaskRowViewModel : ViewModelBase
|
||||
FailureTurnsUsed = t.FailureTurnsUsed;
|
||||
FailureMaxTurns = t.FailureMaxTurns;
|
||||
}
|
||||
|
||||
// Best-effort parse of diff stat strings like "+12 -3" or "12 additions, 3 deletions".
|
||||
private static (int add, int del) ParseDiffStat(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return (0, 0);
|
||||
int add = 0, del = 0;
|
||||
var parts = s.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var p in parts)
|
||||
{
|
||||
if (p.Length > 1 && p[0] == '+' && int.TryParse(p.AsSpan(1), out var a)) add = a;
|
||||
else if (p.Length > 1 && (p[0] == '-' || p[0] == '\u2212') && int.TryParse(p.AsSpan(1), out var d)) del = d;
|
||||
}
|
||||
return (add, del);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ Full flow, invariants, and model/effort/max-turns resolution (including the low-
|
||||
- **ClaudeProcess** — spawns `claude -p --output-format stream-json --verbose --permission-mode auto` (or whatever app settings specify). Prompt via stdin, NDJSON from stdout. CancellationToken kills the process tree. Resolves `_cfg.ClaudeBin` via `ExecutableResolver` (`ClaudeDo.Data`, shared with `ClaudeCliPreflight` and the Installer's checks) before spawning — a `.cmd`/`.bat` shim (e.g. an npm-installed `claude`) is launched through `cmd.exe /c` since `UseShellExecute = false` can't exec a shim directly; a resolved `.exe` starts exactly as before. Throws if nothing resolves. *(Not yet on `main` — see `Environment Checks` in `ClaudeDo.Installer/CLAUDE.md`.)*
|
||||
- **ClaudeArgsBuilder** — `--model`, `--effort`, `--max-turns`, `--append-system-prompt`, `--agents`, `--json-schema`, `--resume`
|
||||
- **StreamAnalyzer** — parses NDJSON; extracts session_id, token counts, turn counts, result text, structured output. Replaced MessageParser.
|
||||
- **WorktreeManager** — worktrees on `claudedo/{taskId[:8]}` branches; commits with semantic messages, updates DB with head commit + diff stats
|
||||
- **WorktreeManager** — worktrees on `claudedo/{id}` branches (full task GUID, dashes stripped — a prefix isn't enough to rule out collisions); commits with semantic messages, updates DB with head commit + diff stats
|
||||
- **CommitMessageBuilder** — `{commitType}(slug): title\n\ndescription\n\nClaudeDo-Task: taskId`; `BuildMerge` is the merge-commit variant (`{commitType}(slug): merge title` + trailer). **Every merge caller passes a blank commit message on purpose** — `TaskMergeService` fills in `BuildMerge` from the task's commit type and its list's name, which is the only place that knows both. Don't reintroduce a caller-side literal.
|
||||
- **TaskResetService** — discards a failed task's worktree, resets the row to Idle, preserves run history
|
||||
- **AgentFileService** — manages `~/.claudeDo/agents/*.md`; list/refresh via SignalR
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using ClaudeDo.Ui.ViewModels.Islands;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.UiVm;
|
||||
|
||||
@@ -35,4 +37,59 @@ public class ParseDiffStatTests
|
||||
Assert.Equal(5, add);
|
||||
Assert.Equal(0, del);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deletions_Only_Returns_Zero_Insertions()
|
||||
{
|
||||
var (add, del) = DetailsIslandViewModel.ParseDiffStat("1 file changed, 4 deletions(-)");
|
||||
Assert.Equal(0, add);
|
||||
Assert.Equal(4, del);
|
||||
}
|
||||
|
||||
// German-Windows git localizes "insertion(s)"/"deletion(s)" (e.g. "Einfügung(en)"/
|
||||
// "Löschung(en)") but keeps the (+)/(-) suffix untranslated — the regex anchors on that.
|
||||
[Fact]
|
||||
public void German_Locale_Stat_Parses_Both()
|
||||
{
|
||||
var (add, del) = DetailsIslandViewModel.ParseDiffStat("2 Dateien geändert, 10 Einfügungen(+), 3 Löschungen(-)");
|
||||
Assert.Equal(10, add);
|
||||
Assert.Equal(3, del);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void German_Locale_Insertions_Only_Returns_Zero_Deletions()
|
||||
{
|
||||
var (add, del) = DetailsIslandViewModel.ParseDiffStat("1 Datei geändert, 5 Einfügung(en)(+)");
|
||||
Assert.Equal(5, add);
|
||||
Assert.Equal(0, del);
|
||||
}
|
||||
|
||||
// TaskRowViewModel used to run its own broken parser (expecting "+12"/"-3" tokens that
|
||||
// `git diff --stat` never emits) — pin it to the same shared, correct parser as the detail badge.
|
||||
[Fact]
|
||||
public void TaskRowViewModel_UpdateFromEntity_Parses_Real_DiffStat_Format()
|
||||
{
|
||||
var task = new TaskEntity
|
||||
{
|
||||
Id = "t1",
|
||||
ListId = "l1",
|
||||
Title = "task",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Status = TaskStatus.Done,
|
||||
Worktree = new WorktreeEntity
|
||||
{
|
||||
TaskId = "t1",
|
||||
Path = "/tmp/wt",
|
||||
BranchName = "claudedo/t1",
|
||||
BaseCommit = "abc",
|
||||
DiffStat = "2 files changed, 10 insertions(+), 3 deletions(-)",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
},
|
||||
};
|
||||
|
||||
var row = TaskRowViewModel.FromEntity(task);
|
||||
|
||||
Assert.Equal(10, row.DiffAdditions);
|
||||
Assert.Equal(3, row.DiffDeletions);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user