Skip to content

feat: synthesize decisions in health reports - #2301

Merged
Asherlc merged 7 commits into
mainfrom
Asherlc/issue-2171-decision-reports
Jul 29, 2026
Merged

Asherlc merged 7 commits into
mainfrom
Asherlc/issue-2171-decision-reports

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • synthesize weekly and monthly report evidence into what changed, likely associations, what worked, what to try next, and confidence/missing-data sections on the server
  • render the same server-owned interpretation on web, mobile, and shared report snapshots while preserving legacy shared snapshots
  • add focused server, web, mobile, and Storybook coverage

Validation

  • pnpm lint
  • pnpm typecheck
  • pnpm spellcheck
  • pnpm knip
  • focused report tests: 49 passed
  • pnpm test: clean full run before the final wording-only accuracy edit; post-edit rerun passed all 14,601 assertions but Vitest exited on a post-run worker RPC timeout (Timeout calling "onTaskUpdate")
  • web Storybook static build
  • mobile Storybook static build

Fixes #2171

Summary by Sourcery

Add server-generated decision-oriented summaries to weekly and monthly health reports and render them consistently across web, mobile, and shared snapshots.

New Features:

  • Introduce deterministic server-side decision synthesis for weekly and monthly health reports, covering changes, associations, successes, next steps, and confidence/missing-data.
  • Display the server-owned decision synthesis in web weekly/monthly reports, mobile reports screen, and new shared report snapshots without client recomputation.

Enhancements:

  • Extend weekly and monthly report schemas and repositories to include optional decisionSupport metadata while preserving backward compatibility for legacy shared snapshots.
  • Update report page subtitles and copy to emphasize decision guidance over raw metrics.
  • Add shared ReportDecisionSynthesis components on web and mobile with Storybook stories for decision-oriented reports.

Documentation:

  • Add a TDD-style plan documenting the decision-oriented report feature, scope, test strategy, and verification steps.

Tests:

  • Add unit tests for weekly and monthly decision synthesis builders to validate phrasing, handling of missing data, and non-causal language.
  • Expand server, web, and mobile report tests to cover presence/absence of decisionSupport, rendering of all synthesis sections, and compatibility with legacy shared reports.

Summary by cubic

Adds server-generated decision synthesis to weekly and monthly health reports and renders it consistently across web, mobile, and shared snapshots. Syncs Expo SDK 57 dependencies and release-age policy to satisfy validation, addressing #2171.

  • New Features

    • Server attaches decisionSupport to weekly/monthly report results and exports ReportDecisionSynthesis from dofek-server/types; @dofek/format/report-decision-synthesis defines shared section order and stable item keys.
    • Web and mobile render a shared ReportDecisionSynthesis before metrics; shared/snapshotted report schemas accept optional decisionSupport and pass it through while preserving legacy snapshots.
  • Bug Fixes

    • Include decisionSupport (or null) in empty-report responses and validate/trim it in web shared-report routes, rejecting blank narratives.
    • Use display-rounded hour pluralization and occurrence-aware keys to prevent “1 hours” and duplicate-key warnings.

Written for commit f979ed1. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added decision summaries to weekly and monthly reports, highlighting changes, likely associations, what worked, next steps, and confidence or missing data.
    • Displayed decision summaries across web, mobile, and shared report views.
    • Preserved compatibility for reports without decision-support data.
  • Bug Fixes

    • Improved rendering when repeated summary messages appear, avoiding duplicate-item warnings.
  • Tests

    • Added coverage for decision-summary content, missing data, report sharing, and weekly/monthly scenarios.

Keep report interpretation server-owned so web, mobile, and shared snapshots present the same evidence and caveats.\n\nRefs #2171
Copilot AI review requested due to automatic review settings July 29, 2026 18:29
@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds server-owned decision synthesis for weekly and monthly health reports and renders it consistently across server, web, mobile, and shared report snapshots while preserving backward compatibility and expanding tests and Storybook coverage.

Sequence diagram for decision synthesis in health reports

sequenceDiagram
  participant Client
  participant WeeklyReportRepository
  participant MonthlyReportRepository
  participant ReportDecisionSynthesisModule

  Client->>WeeklyReportRepository: getReport(weeks, endDate)
  WeeklyReportRepository->>ReportDecisionSynthesisModule: buildWeeklyDecisionSynthesis(current, history)
  WeeklyReportRepository-->>Client: WeeklyReportResult{current, history, decisionSupport}

  Client->>MonthlyReportRepository: getReport(months)
  MonthlyReportRepository->>ReportDecisionSynthesisModule: buildMonthlyDecisionSynthesis(current, history)
  MonthlyReportRepository-->>Client: MonthlyReportResult{current, history, decisionSupport}
Loading

File-Level Changes

Change Details Files
Introduce server-side decision synthesis model and attach it to weekly and monthly report results.
  • Define ReportDecisionSynthesis interface and deterministic weekly/monthly synthesis builders that describe changes, associations, what worked, next steps, and confidence/missing data.
  • Use existing weekly/monthly period summaries as inputs to build descriptive, non-causal narratives, including handling missing data and limited history.
  • Update WeeklyReportRepository and MonthlyReportRepository to include a decisionSupport field in their results and ensure empty reports return null synthesis.
  • Export ReportDecisionSynthesis from server types and add focused unit tests for weekly and monthly synthesis behavior and repository wiring.
packages/server/src/repositories/report-decision-synthesis.ts
packages/server/src/repositories/report-decision-synthesis.test.ts
packages/server/src/repositories/weekly-report-repository.ts
packages/server/src/repositories/weekly-report-repository.test.ts
packages/server/src/repositories/monthly-report-repository.ts
packages/server/src/repositories/monthly-report-repository.test.ts
packages/server/src/types.ts
Render decision synthesis on web weekly/monthly reports and shared report snapshots using a shared UI component.
  • Add a web ReportDecisionSynthesis component that renders all five sections from the server payload in a card, plus Jest/Vitest tests and Storybook stories.
  • Wire WeeklyReportCard and MonthlyReportContent to show decisionSupport above existing metric content when present, keeping layout mostly unchanged.
  • Extend weekly and monthly report Zod schemas and shared health-report route parsing to accept optional decisionSupport, defaulting to null for legacy snapshots.
  • Update web tests to cover rendering of decision support in direct reports and new shared snapshots, and adjust existing tests to account for the new field.
packages/web/src/components/ReportDecisionSynthesis.tsx
packages/web/src/components/ReportDecisionSynthesis.test.tsx
packages/web/src/components/ReportDecisionSynthesis.stories.tsx
packages/web/src/components/WeeklyReportCard.tsx
packages/web/src/components/WeeklyReportCard.test.tsx
packages/web/src/components/WeeklyReportCard.stories.tsx
packages/web/src/components/MonthlyReportContent.tsx
packages/web/src/components/MonthlyReportContent.test.tsx
packages/web/src/components/MonthlyReportContent.stories.tsx
packages/web/src/routes/health-report.tsx
packages/web/src/routes/health-report.test.tsx
packages/web/src/routes/monthly-report.tsx
packages/web/src/routes/weekly-report.tsx
Render decision synthesis on mobile weekly/monthly reports via a shared component and update copy to emphasize decisions.
  • Add a mobile ReportDecisionSynthesis component that renders all decision sections inside a Card with platform-consistent styling, plus tests and Storybook stories.
  • Update the ReportsScreen to render decisionSupport for weekly and monthly data when available, wrapping existing metric cards and preserving loading/empty states.
  • Adjust the mobile reports intro subtitle to focus on what changed, what the data suggests, and what to compare next.
  • Extend TRPC mocks and tests to include decisionSupport, asserting that both weekly and monthly synthesis text is rendered.
packages/mobile/components/ReportDecisionSynthesis.tsx
packages/mobile/components/ReportDecisionSynthesis.test.tsx
packages/mobile/components/ReportDecisionSynthesis.stories.tsx
packages/mobile/app/reports.tsx
packages/mobile/app/reports.test.tsx
Document the decision-oriented reports feature and test-driven development plan.
  • Add a markdown plan outlining goals, behavior, scope, current evidence, test strategy, file structure, and task checklist for decision-oriented reports.
  • Clarify that synthesis is server-owned, deterministic, non-causal, and must remain backward compatible with legacy shared snapshots.
docs/superpowers/plans/2026-07-29-decision-oriented-reports.md

Assessment against linked issues

Issue Objective Addressed Explanation
#2171 Extend weekly and monthly report generation on the server to synthesize decisions (what changed, likely associations, what worked, what to try next, confidence and missing data) alongside existing metric aggregates.
#2171 Update web and mobile clients, including shared-report rendering, to display the server-provided decision synthesis sections while remaining compatible with legacy shared snapshots that lack the new field.
#2171 Document the decision-oriented reports behavior and implementation plan so that the intent and scope of the change are captured for future reference.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Synthesize decision support for weekly/monthly health reports

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Generate deterministic server-side decisionSupport summaries for weekly and monthly reports.
• Render the same server-owned synthesis on web, mobile, and new shared snapshots.
• Preserve legacy shared snapshots and add focused server/web/mobile/Storybook coverage.
Diagram

graph TD
  WR["Weekly repo"] --> SYN["Decision synthesis"] --> API["Health report API"] --> WEB(["Web reports"])
  MR["Monthly repo"] --> SYN --> API
  API --> MOB(["Mobile reports"])
  API --> SNAP[("Shared snapshot")] --> SHARED["Shared report route"] --> WEB
  subgraph Legend
    direction LR
    _svc["Module/Service"] ~~~ _ui(["UI"]) ~~~ _db[("Persisted JSON")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Client-side synthesis (web/mobile)
  • ➕ No server contract/schema changes
  • ➕ Potentially faster iteration on copy per client
  • ➖ Breaks parity across platforms and shared snapshots
  • ➖ Harder to guarantee consistent non-causal language
  • ➖ More duplication and drift risk over time
2. Persist synthesis only at share-time
  • ➕ Shared snapshots become fully self-contained and immutable
  • ➕ Avoids recomputation if synthesis logic changes later
  • ➖ App views (non-shared) still need synthesis generation elsewhere
  • ➖ Mixed behaviors between live reports and shared reports unless also generated on fetch
  • ➖ Doesn’t eliminate the need for a canonical server algorithm
3. LLM-generated narrative summaries
  • ➕ More flexible language and potentially richer summaries
  • ➖ Non-deterministic output complicates testing and trust
  • ➖ Higher operational cost and latency
  • ➖ Harder to enforce “descriptive, non-causal” constraints reliably

Recommendation: Keep the current deterministic, server-owned synthesis approach. It maximizes cross-platform consistency (web/mobile/shared), keeps the domain logic centralized, and is testable for wording and non-causal constraints. The added optional field handling on the shared-report parser is the right compatibility strategy for legacy snapshots.

Files changed (27) +1112 / -143

Enhancement (11) +531 / -136
reports.tsxRender server decision summary on the mobile Reports screen +62/-51

Render server decision summary on the mobile Reports screen

• Imports and conditionally renders ReportDecisionSynthesis above the metric cards for weekly and monthly sections, and updates the subtitle copy to emphasize decision guidance.

packages/mobile/app/reports.tsx

ReportDecisionSynthesis.tsxAdd mobile ReportDecisionSynthesis component +48/-0

Add mobile ReportDecisionSynthesis component

• Creates a render-only component that iterates through the standardized decisionSupport sections and displays each list of server-provided strings in a card.

packages/mobile/components/ReportDecisionSynthesis.tsx

monthly-report-repository.tsAttach decisionSupport to monthly report results +11/-1

Attach decisionSupport to monthly report results

• Extends MonthlyReportResult with an optional server-owned decisionSupport field and computes it via buildMonthlyDecisionSynthesis when a current period exists.

packages/server/src/repositories/monthly-report-repository.ts

report-decision-synthesis.tsAdd deterministic report decision synthesis builders +275/-0

Add deterministic report decision synthesis builders

• Defines the ReportDecisionSynthesis type and implements weekly/monthly builders that generate what changed, associations, what worked, next step, and confidence/missing-data narratives from existing report aggregates/trends.

packages/server/src/repositories/report-decision-synthesis.ts

weekly-report-repository.tsAttach decisionSupport to weekly report results +11/-1

Attach decisionSupport to weekly report results

• Extends WeeklyReportResult with decisionSupport and computes it via buildWeeklyDecisionSynthesis when a current week exists.

packages/server/src/repositories/weekly-report-repository.ts

types.tsExport ReportDecisionSynthesis type for client consumption +1/-0

Export ReportDecisionSynthesis type for client consumption

• Re-exports the server ReportDecisionSynthesis type so web and mobile can type decisionSupport consistently.

packages/server/src/types.ts

MonthlyReportContent.tsxRender decision summary in monthly web report content +2/-0

Render decision summary in monthly web report content

• Adds the shared ReportDecisionSynthesis component and conditionally renders it above the current-month metrics.

packages/web/src/components/MonthlyReportContent.tsx

ReportDecisionSynthesis.tsxAdd web ReportDecisionSynthesis component +32/-0

Add web ReportDecisionSynthesis component

• Creates a render-only component that displays each decisionSupport section with headings and bullet lists, labeled as a "Decision summary" region.

packages/web/src/components/ReportDecisionSynthesis.tsx

WeeklyReportCard.tsxRender decision summary above weekly performance card +87/-81

Render decision summary above weekly performance card

• Wraps the existing card in a vertical layout and conditionally renders ReportDecisionSynthesis when decisionSupport is present, preserving existing metric/status logic.

packages/web/src/components/WeeklyReportCard.tsx

monthly-report.tsxUpdate monthly report page subtitle for decision framing +1/-1

Update monthly report page subtitle for decision framing

• Tweaks page subtitle copy to emphasize decision-making and evidence rather than only trend viewing.

packages/web/src/routes/monthly-report.tsx

weekly-report.tsxUpdate weekly report page subtitle for decision framing +1/-1

Update weekly report page subtitle for decision framing

• Tweaks page subtitle copy to align with the new decision-oriented synthesis sections.

packages/web/src/routes/weekly-report.tsx

Bug fix (1) +22 / -2
health-report.tsxParse optional decisionSupport in shared health report snapshots +22/-2

Parse optional decisionSupport in shared health report snapshots

• Adds a Zod schema for decisionSupport, makes it optional on weekly/monthly snapshot schemas, and normalizes missing fields to null when rendering legacy snapshots.

packages/web/src/routes/health-report.tsx

Tests (10) +364 / -5
reports.test.tsxExtend mobile ReportsScreen tests to include decisionSupport rendering +16/-0

Extend mobile ReportsScreen tests to include decisionSupport rendering

• Updates TRPC mocks to include decisionSupport for weekly and monthly reports and asserts synthesized strings appear on the screen.

packages/mobile/app/reports.test.tsx

ReportDecisionSynthesis.test.tsxAdd unit test for mobile ReportDecisionSynthesis rendering +37/-0

Add unit test for mobile ReportDecisionSynthesis rendering

• Verifies all five sections render and that the component displays server-provided strings without interpreting values.

packages/mobile/components/ReportDecisionSynthesis.test.tsx

monthly-report-repository.test.tsUpdate monthly repository tests for decisionSupport contract +4/-1

Update monthly repository tests for decisionSupport contract

• Adjusts empty-report expectations to include decisionSupport: null and adds assertions that first-month reports include the expected whatChanged copy.

packages/server/src/repositories/monthly-report-repository.test.ts

report-decision-synthesis.test.tsAdd unit tests for weekly/monthly decision synthesis wording and edge cases +203/-0

Add unit tests for weekly/monthly decision synthesis wording and edge cases

• Covers improvements, trade-offs, missing recovery data, insufficient history, and explicit non-causal language across weekly and monthly builders.

packages/server/src/repositories/report-decision-synthesis.test.ts

weekly-report-repository.test.tsUpdate weekly repository tests for decisionSupport contract +2/-1

Update weekly repository tests for decisionSupport contract

• Adjusts empty-report expectations to include decisionSupport: null and adds a basic assertion that non-empty reports include synthesized whatChanged entries.

packages/server/src/repositories/weekly-report-repository.test.ts

health-report.test.tsAssert health report API returns decisionSupport for weekly and monthly +16/-0

Assert health report API returns decisionSupport for weekly and monthly

• Updates router tests to expect decisionSupport sections (including first-period wording and next-step copy) in weekly and monthly report responses.

packages/server/src/routers/health-report.test.ts

MonthlyReportContent.test.tsxUpdate monthly web component tests for decisionSupport and empty contract +9/-1

Update monthly web component tests for decisionSupport and empty contract

• Adds decisionSupport to test data, asserts synthesized text renders, and updates empty-state test to include decisionSupport: null.

packages/web/src/components/MonthlyReportContent.test.tsx

ReportDecisionSynthesis.test.tsxAdd unit test for web ReportDecisionSynthesis rendering +31/-0

Add unit test for web ReportDecisionSynthesis rendering

• Verifies headings for all five sections and that server-provided strings render verbatim.

packages/web/src/components/ReportDecisionSynthesis.test.tsx

WeeklyReportCard.test.tsxUpdate weekly web component tests for decisionSupport presence/absence +11/-0

Update weekly web component tests for decisionSupport presence/absence

• Adds decisionSupport to relevant test data and asserts synthesized text renders, while explicitly setting decisionSupport: null in other scenarios.

packages/web/src/components/WeeklyReportCard.test.tsx

health-report.test.tsxAdd shared report route coverage for new snapshots with decisionSupport +35/-2

Add shared report route coverage for new snapshots with decisionSupport

• Extends mocks/types for WeeklyReportCard and adds a new test ensuring decisionSupport stored in shared snapshots renders in the shared route.

packages/web/src/routes/health-report.test.tsx

Documentation (1) +70 / -0
2026-07-29-decision-oriented-reports.mdAdd TDD plan for decision-oriented weekly/monthly reports +70/-0

Add TDD plan for decision-oriented weekly/monthly reports

• Documents goals, scope constraints, test strategy, file structure, and stepwise tasks for implementing server-owned decision synthesis across platforms.

docs/superpowers/plans/2026-07-29-decision-oriented-reports.md

Other (4) +125 / -0
ReportDecisionSynthesis.stories.tsxAdd Storybook stories for mobile decision synthesis component +45/-0

Add Storybook stories for mobile decision synthesis component

• Introduces baseline and missing-data stories to preview the server-provided decision sections and copy in isolation.

packages/mobile/components/ReportDecisionSynthesis.stories.tsx

MonthlyReportContent.stories.tsxUpdate MonthlyReportContent story to include decisionSupport +16/-0

Update MonthlyReportContent story to include decisionSupport

• Extends Storybook args so monthly report previews render the decision summary section when present.

packages/web/src/components/MonthlyReportContent.stories.tsx

ReportDecisionSynthesis.stories.tsxAdd Storybook stories for web decision synthesis component +45/-0

Add Storybook stories for web decision synthesis component

• Introduces baseline and missing-data stories to preview the standardized decision summary sections and copy.

packages/web/src/components/ReportDecisionSynthesis.stories.tsx

WeeklyReportCard.stories.tsxUpdate WeeklyReportCard story to include decisionSupport +19/-0

Update WeeklyReportCard story to include decisionSupport

• Extends Storybook args so weekly report previews include the decision summary section and representative copy.

packages/web/src/components/WeeklyReportCard.stories.tsx

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

QR code for dofek://preview/pr-2301

Channel pr-2301
Deep Link dofek://preview/pr-2301
Commit 36d3065

To test on device:

  1. Build and install the preview client: PREVIEW_CHANNEL=pr-2301 pnpm expo prebuild --clean -p ios
  2. Or tap deep link on an existing preview build: dofek://preview/pr-2301

Each PR gets its own channel. Build a preview client with PREVIEW_CHANNEL=pr-{N} to test.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 36d30652 are ready:

This comment updates automatically on each PR push.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Reports now receive deterministic server-generated decision-support narratives covering changes, associations, worked patterns, next steps, and confidence or missing data. Web and mobile report surfaces render the narratives while shared snapshots accept legacy data without the new field.

Changes

Decision-oriented report synthesis

Layer / File(s) Summary
Server synthesis generation
packages/server/src/repositories/report-decision-synthesis.ts, packages/server/src/repositories/report-decision-synthesis.test.ts
Weekly and monthly synthesis builders generate and test five narrative sections from current and historical metrics.
Repository and snapshot integration
packages/server/src/repositories/*report-repository.ts, packages/server/src/routers/health-report.test.ts, packages/server/src/types.ts
Report results expose nullable decisionSupport, generated snapshots include baseline narratives, and the type is re-exported.
Web rendering and shared-report compatibility
packages/web/src/components/ReportDecisionSynthesis.*, packages/web/src/components/WeeklyReportCard.*, packages/web/src/components/MonthlyReportContent.*, packages/web/src/routes/health-report.*
Web reports render synthesis sections, shared-report parsing accepts optional nullable decision support, and component/route coverage is updated.
Mobile report rendering
packages/mobile/app/reports.*, packages/mobile/components/ReportDecisionSynthesis.*
Mobile weekly and monthly reports conditionally render the synthesis component with tests and Storybook stories.
Implementation plan
docs/superpowers/plans/2026-07-29-decision-oriented-reports.md
Documents the TDD scope, compatibility requirements, implementation steps, and verification commands.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReportRepository
  participant DecisionSynthesis
  participant WebOrMobileReport
  ReportRepository->>DecisionSynthesis: build synthesis from current and history
  DecisionSynthesis-->>ReportRepository: return decisionSupport
  ReportRepository->>WebOrMobileReport: provide report snapshot
  WebOrMobileReport->>WebOrMobileReport: render five decision sections
Loading

Assessment against linked issues

Objective Addressed Explanation
[2171] Add “what changed”, “likely associations”, “what worked”, “what to try next”, and “confidence and missing data” decision support to reports.

|

Possibly related PRs

Suggested labels: area/server, area/web, area/mobile, type/feature

Suggested reviewers: cubic-dev-ai

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is relevant, but it violates the required area-prefix format for this cross-cutting change. Rewrite it in imperative mood with an area prefix, e.g. [server] synthesize decisions in health reports.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

qodo-code-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 202 rules

Grey Divider


Remediation recommended

1. Plan omits mise run test:sandbox ✓ Resolved 📘 Rule violation ▣ Testability
Description
The new TDD plan documents verification using rtk pnpm ... commands but does not use the
standardized Codex cloud sandbox verification entrypoint mise run test:sandbox (and does not
reference SANDBOX=1 mise run cloud:init). This can lead to non-standard or incomplete sandbox
verification steps being followed.
Code

docs/superpowers/plans/2026-07-29-decision-oriented-reports.md[R65-69]

+- [ ] Run `rtk pnpm lint`.
+- [ ] Run `rtk pnpm tsc --noEmit`.
+- [ ] Run `rtk pnpm --dir packages/server tsc --noEmit`.
+- [ ] Run `rtk pnpm --dir packages/web tsc --noEmit`.
+- [ ] Run `rtk pnpm test`.
Relevance

●●● Strong

Repo standardizes sandbox verification via mise run test:sandbox; similar doc/task alignment
accepted previously.

PR-#2244

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2384980 requires Codex cloud sandbox initialization and verification instructions
to use SANDBOX=1 mise run cloud:init and mise run test:sandbox. The newly added plan’s “Final
Verification” section instead instructs running rtk pnpm lint, rtk pnpm tsc --noEmit, and `rtk
pnpm test`.

Rule 2384980: Use standardized mise tasks for Codex cloud sandbox initialization and verification
docs/superpowers/plans/2026-07-29-decision-oriented-reports.md[65-69]
AGENTS.md[267-273]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new plan’s verification steps use `rtk pnpm ...` commands instead of the standardized Codex cloud sandbox commands required by policy (`SANDBOX=1 mise run cloud:init` for init and `mise run test:sandbox` for verification).

## Issue Context
This plan lives under `docs/superpowers/plans/` and includes explicit “Final Verification” instructions. Per the compliance checklist, sandbox workflows must be documented using the standardized mise tasks.

## Fix Focus Areas
- docs/superpowers/plans/2026-07-29-decision-oriented-reports.md[65-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Duplicate React list keys ✓ Resolved 🐞 Bug ☼ Reliability
Description
ReportDecisionSynthesis uses the bullet text as the React key (key={item}). If a decision section
ever contains duplicate strings, React will emit duplicate-key warnings and may reconcile list items
incorrectly across renders.
Code

packages/web/src/components/ReportDecisionSynthesis.tsx[R23-25]

+              {synthesis[section.key].map((item) => (
+                <li key={item}>{item}</li>
+              ))}
Relevance

●●● Strong

They previously required unique React list keys to avoid collisions; likely will append index/unique
suffix here too.

PR-#1868

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both new components map string[] sections and use the string value as the React key, which is not
guaranteed to be unique within a section.

packages/web/src/components/ReportDecisionSynthesis.tsx[19-26]
packages/mobile/components/ReportDecisionSynthesis.tsx[20-27]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ReportDecisionSynthesis` renders list items with `key={item}` where `item` is an arbitrary server-provided string. Keys are not guaranteed unique, so duplicate strings in a section can produce duplicate sibling keys and unreliable reconciliation.

### Issue Context
This same pattern exists in both web and mobile implementations.

### Fix Focus Areas
- packages/web/src/components/ReportDecisionSynthesis.tsx[19-26]
- packages/mobile/components/ReportDecisionSynthesis.tsx[20-28]

### Suggested fix
Within each section, key list items by index (or by a composite key including the section key + index), e.g.:
- Web: `{synthesis[section.key].map((item, idx) => <li key={`${section.key}-${idx}`}>{item}</li>)}`
- Mobile: `{synthesis[section.key].map((item, idx) => <Text key={`${section.key}-${idx}`}>{item}</Text>)}`
This preserves stable uniqueness even when strings repeat.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Hour pluralization mismatch ✓ Resolved 🐞 Bug ≡ Correctness
Description
formatHours() pluralizes using the raw hours value (hours === 1) but displays a rounded value
from Intl.NumberFormat. For values like 0.96 (which can round to "1"), decisionSupport text can
render as "1 hours".
Code

packages/server/src/repositories/report-decision-synthesis.ts[R24-31]

+function formatNumber(value: number, maximumFractionDigits = 1): string {
+  return new Intl.NumberFormat("en-US", { maximumFractionDigits }).format(value);
+}
+
+function formatHours(hours: number): string {
+  const value = formatNumber(hours);
+  return `${value} ${hours === 1 ? "hour" : "hours"}`;
+}
Relevance

●●● Strong

Team has accepted fixing singular/plural edge cases in server-generated copy; this prevents “1
hours” outputs.

PR-#1875

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code rounds the displayed number via Intl.NumberFormat but checks singular vs plural using
strict equality against the unrounded input float.

packages/server/src/repositories/report-decision-synthesis.ts[24-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`formatHours(hours)` formats a *rounded* display string but chooses singular/plural using the *unrounded* float, which can produce user-facing grammar errors like `"1 hours"`.

### Issue Context
Training hours come from aggregated floating-point sums; values close to 1 can round to `"1"` while failing `hours === 1`.

### Fix Focus Areas
- packages/server/src/repositories/report-decision-synthesis.ts[24-31]

### Suggested fix
Compute a rounded numeric value first (matching the displayed precision), then use that value for both formatting and pluralization, e.g.:
```ts
function formatHours(hours: number): string {
 const rounded = Math.round(hours * 10) / 10;
 const value = formatNumber(rounded);
 return `${value} ${rounded === 1 ? "hour" : "hours"}`;
}
```
Add/extend a unit test for `formatHours(0.96)` (or a synthesis case that yields ~0.96 hours) asserting it outputs `"1 hour"`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread docs/superpowers/plans/2026-07-29-decision-oriented-reports.md Outdated
Comment thread packages/web/src/components/ReportDecisionSynthesis.tsx Outdated
Comment thread packages/server/src/repositories/report-decision-synthesis.ts
Use display-rounded pluralization and occurrence-aware React keys, and document the canonical sandbox verification path.\n\nRefs #2171
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Exercise decision boundaries and missing-data combinations through the public builders so Stryker verifies the intended behavior.
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-07-29-decision-oriented-reports.md`:
- Line 5: Update the plan’s terminology from “iOS” to “mobile” consistently,
including the behavior, evidence, test, and task sections referenced in the
comment. Preserve explicit web/mobile parity and identify the concrete mobile
implementation files and verification commands rather than narrowing scope to
iOS.

In `@packages/mobile/app/reports.test.tsx`:
- Around line 38-44: Add a mobile reports test variant with decisionSupport set
to null, exercising the null guard in reports.tsx. Assert that “Decision
summary” is absent while the metric card still renders, preserving coverage of
the empty decision-support path.

In `@packages/mobile/components/ReportDecisionSynthesis.tsx`:
- Around line 6-24: Extract the shared sections table and keyedItems logic from
ReportDecisionSynthesis into `@dofek/format`, exposing shared symbols such as
reportDecisionSynthesisSections and keyedDecisionSynthesisItems. Update both the
mobile and web ReportDecisionSynthesis components to import and use these
exports, removing their local duplicated definitions while preserving the
existing keys, titles, and deduplication behavior.

In `@packages/server/src/repositories/report-decision-synthesis.ts`:
- Around line 194-200: Update the confidence message in the report-decision
synthesis flow so cases with periodCount >= 4 and missing.length > 0 describe
the missing metric as the limiting factor rather than attributing limited
confidence to the number of periods. Branch the reason on missing.length > 0
while preserving the existing moderate-confidence message when no metrics are
missing and the low/limited period-count wording when fewer than four periods
are available.
- Around line 34-42: Update formatDuration to return a non-empty duration for
positive sub-minute inputs that round to zero, while preserving the existing
hour/minute formatting for values rounding to at least one minute. Ensure
describeWeeklySleepChange callers never receive an empty string for valid
positive sleep durations.

In `@packages/web/src/components/ReportDecisionSynthesis.stories.tsx`:
- Around line 24-45: Add an Empty story alongside Default and MissingData in the
ReportDecisionSynthesis stories, passing a synthesis fixture with all section
arrays empty to cover the no-data state. Do not add a loading prop or loading
story, since loading remains parent-owned and ReportDecisionSynthesis has no
loading contract.

In `@packages/web/src/routes/health-report.tsx`:
- Around line 50-56: Update reportDecisionSynthesisSchema so each narrative
string in its arrays must be non-empty or contain text, while preserving support
for empty arrays. Apply the validation consistently to whatChanged,
likelyAssociations, whatWorked, whatToTryNext, and confidenceAndMissingData
before ReportDecisionSynthesis renders the items.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e9e055f2-7ab5-4c75-a110-2e165e4fc3eb

📥 Commits

Reviewing files that changed from the base of the PR and between a11b457 and cb5a7c0.

📒 Files selected for processing (27)
  • docs/superpowers/plans/2026-07-29-decision-oriented-reports.md
  • packages/mobile/app/reports.test.tsx
  • packages/mobile/app/reports.tsx
  • packages/mobile/components/ReportDecisionSynthesis.stories.tsx
  • packages/mobile/components/ReportDecisionSynthesis.test.tsx
  • packages/mobile/components/ReportDecisionSynthesis.tsx
  • packages/server/src/repositories/monthly-report-repository.test.ts
  • packages/server/src/repositories/monthly-report-repository.ts
  • packages/server/src/repositories/report-decision-synthesis.test.ts
  • packages/server/src/repositories/report-decision-synthesis.ts
  • packages/server/src/repositories/weekly-report-repository.test.ts
  • packages/server/src/repositories/weekly-report-repository.ts
  • packages/server/src/routers/health-report.test.ts
  • packages/server/src/types.ts
  • packages/web/src/components/MonthlyReportContent.stories.tsx
  • packages/web/src/components/MonthlyReportContent.test.tsx
  • packages/web/src/components/MonthlyReportContent.tsx
  • packages/web/src/components/ReportDecisionSynthesis.stories.tsx
  • packages/web/src/components/ReportDecisionSynthesis.test.tsx
  • packages/web/src/components/ReportDecisionSynthesis.tsx
  • packages/web/src/components/WeeklyReportCard.stories.tsx
  • packages/web/src/components/WeeklyReportCard.test.tsx
  • packages/web/src/components/WeeklyReportCard.tsx
  • packages/web/src/routes/health-report.test.tsx
  • packages/web/src/routes/health-report.tsx
  • packages/web/src/routes/monthly-report.tsx
  • packages/web/src/routes/weekly-report.tsx

Comment thread docs/superpowers/plans/2026-07-29-decision-oriented-reports.md Outdated
Comment thread packages/mobile/app/reports.test.tsx Outdated
Comment thread packages/mobile/components/ReportDecisionSynthesis.tsx Outdated
Comment thread packages/server/src/repositories/report-decision-synthesis.ts
Comment thread packages/server/src/repositories/report-decision-synthesis.ts Outdated
Comment thread packages/web/src/components/ReportDecisionSynthesis.stories.tsx
Comment thread packages/web/src/routes/health-report.tsx
Expect the server-owned decision support field in empty weekly and monthly report responses.
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Update the SDK 57 compatibility set and release-age policy entries required by Expo dependency validation.
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@Asherlc
Asherlc merged commit bafd2e6 into main Jul 29, 2026
104 checks passed
@Asherlc
Asherlc deleted the Asherlc/issue-2171-decision-reports branch July 29, 2026 21:00
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.

[REPORT-02] Reports repeat dashboards instead of synthesizing decisions

2 participants