fix(worker): sanitize report model arg, fix multi-repo summary attribution and standup-weekday sentinel
This commit is contained in:
@@ -52,7 +52,7 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
|||||||
? @"C:\Private"
|
? @"C:\Private"
|
||||||
: string.Join(Environment.NewLine,
|
: string.Join(Environment.NewLine,
|
||||||
System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new());
|
System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new());
|
||||||
General.StandupWeekday = dto.StandupWeekday == 0 ? (int)DayOfWeek.Wednesday : dto.StandupWeekday;
|
General.StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday;
|
||||||
}
|
}
|
||||||
else StatusMessage = "Worker offline — settings read-only.";
|
else StatusMessage = "Worker offline — settings read-only.";
|
||||||
|
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
|||||||
WorktreeAutoCleanupEnabled = dto.WorktreeAutoCleanupEnabled,
|
WorktreeAutoCleanupEnabled = dto.WorktreeAutoCleanupEnabled,
|
||||||
WorktreeAutoCleanupDays = dto.WorktreeAutoCleanupDays,
|
WorktreeAutoCleanupDays = dto.WorktreeAutoCleanupDays,
|
||||||
ReportExcludedPaths = dto.ReportExcludedPaths,
|
ReportExcludedPaths = dto.ReportExcludedPaths,
|
||||||
StandupWeekday = dto.StandupWeekday == 0 ? (int)DayOfWeek.Wednesday : dto.StandupWeekday,
|
StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,14 @@ public sealed class ClaudeHistoryReader : IClaudeHistoryReader
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Keep only the closing summary per (repo, day). If this turn moved to a
|
||||||
|
// different repo/day (e.g. the session cd'd), flush the previous one first.
|
||||||
|
if (lastAssistantText is not null &&
|
||||||
|
(cwd != lastAssistantRepo || date != lastAssistantDate))
|
||||||
|
{
|
||||||
|
Bucket(buckets, lastAssistantRepo!, lastAssistantDate).Summaries.Add(lastAssistantText);
|
||||||
|
lastAssistantText = null;
|
||||||
|
}
|
||||||
lastAssistantText = text.Trim();
|
lastAssistantText = text.Trim();
|
||||||
lastAssistantRepo = cwd;
|
lastAssistantRepo = cwd;
|
||||||
lastAssistantDate = date;
|
lastAssistantDate = date;
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ public sealed class WeekReportService : IWeekReportService
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var prompt = WeekReportPromptBuilder.Build(start, end, activity, notesByDay);
|
var prompt = WeekReportPromptBuilder.Build(start, end, activity, notesByDay);
|
||||||
var args = $"-p --output-format stream-json --verbose --permission-mode auto --model {model}";
|
// Guard against argument injection via the model setting: model aliases/ids are
|
||||||
|
// alphanumerics, dashes and dots only.
|
||||||
|
var safeModel = new string(model.Where(c => char.IsLetterOrDigit(c) || c is '-' or '.').ToArray());
|
||||||
|
if (safeModel.Length == 0) safeModel = "sonnet";
|
||||||
|
var args = $"-p --output-format stream-json --verbose --permission-mode auto --model {safeModel}";
|
||||||
var result = await _claude.RunAsync(args, prompt, Path.GetTempPath(), _ => Task.CompletedTask, ct);
|
var result = await _claude.RunAsync(args, prompt, Path.GetTempPath(), _ => Task.CompletedTask, ct);
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
throw new InvalidOperationException(result.ErrorMarkdown ?? "Claude konnte den Bericht nicht erzeugen.");
|
throw new InvalidOperationException(result.ErrorMarkdown ?? "Claude konnte den Bericht nicht erzeugen.");
|
||||||
|
|||||||
@@ -27,6 +27,22 @@ public class ClaudeHistoryReaderTests : IDisposable
|
|||||||
|
|
||||||
private static string Json(string s) => System.Text.Json.JsonSerializer.Serialize(s);
|
private static string Json(string s) => System.Text.Json.JsonSerializer.Serialize(s);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SessionSpanningTwoRepos_KeepsLastSummaryPerRepo()
|
||||||
|
{
|
||||||
|
WriteSession("proj", "s.jsonl",
|
||||||
|
AssistantLine(@"C:\Dev\Repos\A", "2026-06-01T08:00:00Z", "summary A"),
|
||||||
|
AssistantLine(@"C:\Dev\Repos\B", "2026-06-01T09:00:00Z", "summary B"));
|
||||||
|
|
||||||
|
var reader = new ClaudeHistoryReader(_root);
|
||||||
|
var result = await reader.ReadAsync(new DateOnly(2026, 6, 1), new DateOnly(2026, 6, 3),
|
||||||
|
Array.Empty<string>());
|
||||||
|
|
||||||
|
Assert.Equal(2, result.Count);
|
||||||
|
Assert.Equal(new[] { "summary A" }, result.Single(r => r.RepoPath.EndsWith("A")).Days.Single().Summaries);
|
||||||
|
Assert.Equal(new[] { "summary B" }, result.Single(r => r.RepoPath.EndsWith("B")).Days.Single().Summaries);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Extracts_Prompts_And_Last_Assistant_Summary_GroupedByRepoAndDay()
|
public async Task Extracts_Prompts_And_Last_Assistant_Summary_GroupedByRepoAndDay()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user