feat(worker): week report prompt builder (day-major pivot)
This commit is contained in:
71
src/ClaudeDo.Worker/Report/WeekReportPromptBuilder.cs
Normal file
71
src/ClaudeDo.Worker/Report/WeekReportPromptBuilder.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Worker.Report;
|
||||||
|
|
||||||
|
public static class WeekReportPromptBuilder
|
||||||
|
{
|
||||||
|
private const string Instructions = """
|
||||||
|
You are generating a concise weekly standup report for a software developer.
|
||||||
|
Summarize what they accomplished between {0} and {1}.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Write the ENTIRE report in German.
|
||||||
|
- Group by day. One "## {{Wochentag}}, {{dd.MM.yyyy}}" section per day that has
|
||||||
|
activity (German weekday names). Omit days with no activity entirely.
|
||||||
|
- Within each day: 3-5 first-person, past-tense bullets ("- Habe X umgesetzt",
|
||||||
|
"- Y behoben"). Merge related small work into one bullet.
|
||||||
|
- Drop trivia: typo fixes, pure exploration, false starts, tooling/log noise.
|
||||||
|
- Blend the developer's own notes and the derived activity into ONE deduplicated
|
||||||
|
bullet list per day. The developer's notes are authoritative - never omit or
|
||||||
|
contradict their substance.
|
||||||
|
- Name the project/repo when it adds clarity.
|
||||||
|
- Output ONLY the dated sections. No preamble, no intro, no closing remarks.
|
||||||
|
""";
|
||||||
|
|
||||||
|
public static string Build(
|
||||||
|
DateOnly start, DateOnly end,
|
||||||
|
IReadOnlyList<RepoActivity> activity,
|
||||||
|
IReadOnlyDictionary<DateOnly, List<string>> notesByDay)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, Instructions,
|
||||||
|
start.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture),
|
||||||
|
end.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture)));
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
var days = new SortedDictionary<DateOnly, List<(string Repo, DayActivity Day)>>();
|
||||||
|
foreach (var repo in activity)
|
||||||
|
foreach (var day in repo.Days)
|
||||||
|
{
|
||||||
|
if (!days.TryGetValue(day.Date, out var list))
|
||||||
|
days[day.Date] = list = new();
|
||||||
|
list.Add((repo.RepoPath, day));
|
||||||
|
}
|
||||||
|
foreach (var d in notesByDay.Keys)
|
||||||
|
if (!days.ContainsKey(d)) days[d] = new();
|
||||||
|
|
||||||
|
sb.AppendLine("== Activity (from session history) ==");
|
||||||
|
foreach (var (date, repos) in days)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"### {date:yyyy-MM-dd}");
|
||||||
|
foreach (var (repoPath, day) in repos)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Repo: {repoPath}");
|
||||||
|
foreach (var p in day.Prompts) sb.AppendLine($" Prompt: {p}");
|
||||||
|
foreach (var s in day.Summaries) sb.AppendLine($" Summary: {s}");
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine("== Developer notes ==");
|
||||||
|
foreach (var (date, _) in days)
|
||||||
|
{
|
||||||
|
if (!notesByDay.TryGetValue(date, out var notes) || notes.Count == 0) continue;
|
||||||
|
sb.AppendLine($"### {date:yyyy-MM-dd}");
|
||||||
|
foreach (var n in notes) sb.AppendLine($" - {n}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using ClaudeDo.Worker.Report;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Worker.Tests.Report;
|
||||||
|
|
||||||
|
public class WeekReportPromptBuilderTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Build_IsDayMajor_AndIncludesNotesAndInstructions()
|
||||||
|
{
|
||||||
|
var repoA = new RepoActivity { RepoPath = @"C:\Dev\App" };
|
||||||
|
var d1 = new DayActivity { Date = new DateOnly(2026, 6, 1) };
|
||||||
|
d1.Prompts.Add("Add login");
|
||||||
|
d1.Summaries.Add("Implemented login");
|
||||||
|
repoA.Days.Add(d1);
|
||||||
|
var d2 = new DayActivity { Date = new DateOnly(2026, 6, 2) };
|
||||||
|
d2.Prompts.Add("Fix bug");
|
||||||
|
repoA.Days.Add(d2);
|
||||||
|
|
||||||
|
var notes = new Dictionary<DateOnly, List<string>>
|
||||||
|
{
|
||||||
|
[new DateOnly(2026, 6, 1)] = new() { "Standup um 9" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var prompt = WeekReportPromptBuilder.Build(
|
||||||
|
new DateOnly(2026, 5, 28), new DateOnly(2026, 6, 3),
|
||||||
|
new[] { repoA }, notes);
|
||||||
|
|
||||||
|
Assert.Contains("Write the ENTIRE report in German", prompt);
|
||||||
|
var idxJun1 = prompt.IndexOf("2026-06-01", StringComparison.Ordinal);
|
||||||
|
var idxJun2 = prompt.IndexOf("2026-06-02", StringComparison.Ordinal);
|
||||||
|
Assert.True(idxJun1 >= 0 && idxJun2 > idxJun1);
|
||||||
|
Assert.Contains("Add login", prompt);
|
||||||
|
Assert.Contains("Implemented login", prompt);
|
||||||
|
Assert.Contains("Standup um 9", prompt);
|
||||||
|
Assert.Contains(@"C:\Dev\App", prompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user