Skip to content

feat(retrospective): structured metrics + narrative contract with empty-period guard - #207

Merged
thomasluizon merged 1 commit into
mainfrom
feature/retrospective-dashboard
Jun 15, 2026
Merged

feat(retrospective): structured metrics + narrative contract with empty-period guard#207
thomasluizon merged 1 commit into
mainfrom
feature/retrospective-dashboard

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Backend for the retrospective rework, paired with the orbit-ui-mobile UX/QA batch.

What

Rework GetRetrospectiveQuery to return computed metrics plus a structured narrative instead of freeform LLM text, so the client can render a stats dashboard.

  • Metrics: completion rate, totals, active days, current/best streak, bad-habit slips, weekly consistency, top habits, needs-attention
  • RetrospectiveMetricsCalculator computes the metrics from habit logs
  • RetrospectiveNarrative domain model for the structured sections
  • AiRetrospectiveService builds the narrative; camelCase JSON serialization
  • Empty-period guard: when a period has no completions and no bad-habit slips, fail with NoHabitsForPeriod (no LLM call) so the UI shows a dedicated empty state instead of a hallucinated summary

Verification

  • Build: succeeded
  • Tests: 3477 passed (Domain 386 / Application 1988 / Infrastructure 1103), 0 failed

Paired PR

🤖 Generated with Claude Code

…ty-period guard

Rework GetRetrospectiveQuery to return computed metrics (completion rate,
totals, active days, current/best streak, bad-habit slips, weekly consistency,
top habits, needs-attention) plus a structured narrative, replacing the
freeform LLM text so the client can render a stats dashboard.

Add an empty-period guard: when a period has no completions and no bad-habit
slips, fail with NoHabitsForPeriod instead of calling the LLM, so the UI shows
a dedicated empty state rather than a hallucinated summary.

- RetrospectiveMetricsCalculator computes metrics from habit logs
- RetrospectiveNarrative domain model for the structured sections
- AiRetrospectiveService builds the narrative; camelCase JSON serialization
- Tests updated + added for the empty-period failure path

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clean, well-scoped rework. The structured metrics + narrative split is the right design — deterministic numbers come from RetrospectiveMetricsCalculator and the LLM only touches prose, so neither can corrupt the other. A few things I verified:

  • Cache key versioned to retro:v2: — avoids serving the old cached string to the new client shape.
  • Logs are date-filtered at the EF Include level (line 68 of GetRetrospectiveQuery.cs), so AccumulateWeekdayConsistency building its completedDates HashSet from habit.Logs without a redundant date filter is correct.
  • Empty-period guard correctly gates on both completions AND bad-habit slips before invoking the LLM; the fallback path stores nothing in cache, which is right.
  • ParseNarrative fallback — partial-heading or unparseable AI output lands cleanly in Highlights with empty siblings; the UI always gets a valid payload.
  • streakState?.CurrentStreak ?? 0 — null-safe for new users without streak history.
  • Test coverage — handler tests exercise the calculator end-to-end (static, no interface), covering top/needs-attention ordering, weekday consistency, bad-habit slip isolation, emoji passthrough, and the cache-hit/miss contract. AiRetrospectiveServiceTests covers the narrative parser with English, Portuguese, numbered headings, and fallback cases.

Authorization, validation, and CLAUDE.md rules all look good. Pairing this with orbit-ui-mobile #175 closes the contract loop.

@thomasluizon
thomasluizon merged commit ada1e98 into main Jun 15, 2026
6 checks passed
@thomasluizon
thomasluizon deleted the feature/retrospective-dashboard branch June 15, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant