Files
ClaudeDo/docs/superpowers/specs/2026-06-03-weekly-report-design.md
2026-06-03 08:52:25 +02:00

11 KiB
Raw Blame History

Weekly Report — Design

Date: 2026-06-03 Status: Approved (pending spec review)

Goal

Generate a short, standup-focused report of what the user did over the past week, for the Wednesday standup. The report is built from the user's Claude Code session history across all repos, distilled and summarized by Claude. Personal repos under a configurable excluded path (default C:\Private) are left out. The user can author per-day bullet notes inside ClaudeDo (via the My Day list) that are folded into the report.

Decisions (from brainstorming)

  • Data source: all Claude Code history in ~/.claude/projects/*/*.jsonl, both manual sessions and ClaudeDo-run tasks, grouped by repo.
  • Exclusion: a configurable list of path prefixes (default ["C:\\Private"]). Any session whose cwd starts with an excluded prefix is dropped.
  • Summarization: Claude CLI summarizes. The Worker distills the logs, then runs a single one-shot claude -p call via the existing ClaudeProcess and returns the result markdown. No worktree, no task row, no queue.
  • Period: default "since last Wednesday → today", computed from a configurable standup weekday. The range is adjustable in the modal.
  • Signal fed to Claude: user prompts (intent), assistant closing summaries, and the user's daily notes. No git-commit scanning.
  • Report shape: German, grouped by day, first-person past-tense bullets, ~3-5 bullets/day with trivia merged/dropped, notes blended into one deduplicated list per day. See the Report Prompt section.
  • Placement: a "Weekly Report" overlay modal opened from the toolbar, rendering via the existing MarkdownView.
  • Output: view-only in-app (no export).
  • Notes UI: authored in the My Day list via a pinned non-task "Notes" pseudo-row that repurposes the Details island into a bullet-notes editor. Per-day bullets with a day navigator (prev/next arrows + date picker + Today).
  • Report persistence: generated reports are stored, keyed by exact date range, and reused. Generation is button-driven (never automatic); a Regenerate button overwrites.

Architecture Overview

UI (WeeklyReportModal, Details-island notes mode)
        │  SignalR
        ▼
WorkerHub  ── GetWeekReport / GenerateWeekReport / daily-notes CRUD
        │
        ├── WeekReportService  ──► ClaudeHistoryReader (scan ~/.claude/projects)
        │        │                 (distilled activity)
        │        ├── DailyNoteRepository (notes in window)
        │        ├── ClaudeProcess (one-shot summarize)
        │        └── WeekReportRepository (store/reuse)
        └── DailyNoteRepository (CRUD)

Data: DailyNoteEntity, WeekReportEntity + repositories + EF migration
      AppSettingsEntity: ReportExcludedPaths, StandupWeekday

Components

1. Data layer (ClaudeDo.Data)

DailyNoteEntity (table daily_notes)

  • Id (GUID string, init-only PK)
  • Date (date-only; the day the bullet belongs to)
  • Text (string, the bullet content)
  • SortOrder (int; ordering within a day)
  • CreatedAt (DateTime)

DailyNoteRepository (async, CancellationToken, follows existing repo pattern)

  • ListByDayAsync(DateOnly day) — bullets for one day, ordered by SortOrder.
  • ListBetweenAsync(DateOnly start, DateOnly end) — bullets in a window (used by the report).
  • AddAsync(DateOnly day, string text) — appends a bullet (assigns next SortOrder).
  • UpdateAsync(string id, string text)
  • DeleteAsync(string id)

WeekReportEntity (table week_reports)

  • Id (GUID string, init-only PK)
  • StartDate, EndDate (date-only; the report window — unique together)
  • Markdown (string; the generated report)
  • GeneratedAt (DateTime)

WeekReportRepository

  • GetByRangeAsync(DateOnly start, DateOnly end) — stored report for an exact range, or null.
  • UpsertAsync(DateOnly start, DateOnly end, string markdown) — insert or overwrite by range.

AppSettingsEntity — two new columns:

  • ReportExcludedPaths (string, JSON array of path prefixes; default ["C:\\Private"])
  • StandupWeekday (int, DayOfWeek; default Wednesday = 3)

Migration — one EF migration adds daily_notes, week_reports, and the two app_settings columns. Entity configs in Configuration/ (date-only and enum/JSON conversion via ValueConverter, per existing convention).

2. Worker (ClaudeDo.Worker) — new Report/ folder

ClaudeHistoryReader (raw → distilled)

  • Input: date window + excluded path prefixes.
  • Enumerates ~/.claude/projects/*/*.jsonl.
  • Parses each line as JSON; tolerant of malformed lines (skip, never throw).
  • Drops a session entirely if its cwd starts with any excluded prefix (case-insensitive, normalized separators).
  • Keeps messages whose timestamp falls in [start, end].
  • Extracts, per repo (cwd) → per day:
    • user prompts: type == "user" text content (string or content[].text). Skip tool-result-only user turns and queue/attachment/hook noise.
    • assistant closing summaries: the final assistant text block of each turn/session.
  • Output: a structured model, e.g. IReadOnlyList<RepoActivity> where RepoActivity { RepoPath, Days: List<DayActivity{ Date, Prompts[], Summaries[] }> }.

WeekReportService (distilled → stored summary)

  • GenerateAsync(start, end, ct):
    1. Read settings (excluded paths, standup weekday).
    2. ClaudeHistoryReader → distilled activity.
    3. DailyNoteRepository.ListBetweenAsync → notes grouped by day.
    4. Pivot the distilled activity (repo→day from the reader) into day-major (day→repo) to match the day-grouped report, and build the prompt from the template in the Report Prompt section. Empty window → produce a "no activity" report without calling Claude.
    5. Run ClaudeProcess once (claude -p, no worktree/agents; working dir = a neutral dir). Read RunResult.ResultMarkdown.
    6. WeekReportRepository.UpsertAsync(start, end, markdown); return markdown.
    7. On Claude failure, surface RunResult.ErrorMarkdown to the caller (do not store).
  • GetStoredAsync(start, end)WeekReportRepository.GetByRangeAsync.

Interfaces live in Report/Interfaces/ per the area convention.

Report Prompt

WeekReportService assembles this prompt. Instructions are in English (more reliable steering); the output is forced to German. {...} are filled at build time.

You are generating a concise weekly standup report for a software developer.
Summarize what they accomplished between {start:dd.MM.yyyy} and {end:dd.MM.yyyy}.

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: 35 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.

== Activity (from session history) ==
{day-major: for each day → for each repo → its prompts + closing summaries}

== Developer notes ==
{day-major: for each day → the bullets}

3. IPC (Hub + WorkerClient)

WorkerHub new methods:

  • GetWeekReport(string startIso, string endIso) → stored markdown or null.
  • GenerateWeekReport(string startIso, string endIso) → generates, stores, returns markdown.
  • GetDailyNotes(string dayIso) → bullets for a day.
  • AddDailyNote(string dayIso, string text) → created bullet.
  • UpdateDailyNote(string id, string text).
  • DeleteDailyNote(string id).

WorkerClient (UI) mirrors these, following the existing WorkerPrimeScheduleApi/AppSettings method pattern.

4. UI (ClaudeDo.Ui)

Weekly Report modal (WeeklyReportModalView + WeeklyReportModalViewModel)

  • Overlay modal in the Modals/ pattern (like WorktreesOverviewModalView), registered in IslandsShellViewModel, opened from a new toolbar button.
  • Date range: two ThemedDatePickers, default "since last Wednesday → today" computed from StandupWeekday.
  • On open and on range change: call GetWeekReport.
    • Stored report exists → render markdown via MarkdownView, show GeneratedAt, show a Regenerate button.
    • None → empty state ("Not generated yet") + a Generate button.
  • Generate/Regenerate: call GenerateWeekReport with a busy/spinner state; render the returned markdown. Generation only ever runs from these buttons.
  • View-only; no export.

Notes in My Day

  • The My Day smart list (smart:my-day) pins a fixed, non-task "Notes" pseudo-row at the top, recognized by the list/selection code (not a TaskEntity).
  • Selecting it puts the Details island into notes mode (task fields hidden, notes editor shown). The island hosts a dedicated NotesEditorViewModel + small view rather than swelling DetailsIslandViewModel (already ~978 lines); the bullet logic stays isolated and testable.
  • Day navigator in the editor header: < / > arrows to step days, a ThemedDatePicker to jump to any date, and a "Today" button. Defaults to today; the pinned row's default day rolls over at midnight (no data lost — past days remain reachable via the navigator).
  • Bullet editing for the selected day: list of bullets with add / inline-edit / delete / reorder (SortOrder). Each operation goes through the daily-notes hub CRUD.

5. Settings

  • Add the excluded-path list and the standup weekday to the existing Settings modal, persisted via the new app_settings columns and the existing GetAppSettings/UpdateAppSettings path.

Error Handling

  • Malformed/unreadable JSONL lines are skipped, never fatal.
  • Empty window → a "no activity" report, no Claude call.
  • Claude call failure → error surfaced in the modal; nothing stored.
  • Date ranges normalized to date-only; the stored report key is the exact (start, end).

Testing

  • ClaudeHistoryReader (Worker tests, fixture .jsonl): date-window filtering, excluded-prefix dropping (case/separator normalization), prompt/summary extraction, malformed-line tolerance, repo/day grouping.
  • WeekReportService: prompt-building from distilled activity + notes; empty-window short-circuit; storage upsert; with a faked ClaudeProcess.
  • DailyNoteRepository and WeekReportRepository: CRUD / upsert / range lookup against real SQLite (matches existing test style).

Out of Scope

  • Report export (clipboard/file) — view-only for now.
  • Git-commit scanning.
  • Editing or summarizing full transcripts; only prompts + closing summaries are used.