Skip to content

feat(body): add measurement decision context - #2417

Merged
Asherlc merged 11 commits into
mainfrom
codex/issue-2145
Aug 3, 2026
Merged

Asherlc merged 11 commits into
mainfrom
codex/issue-2145

Conversation

@Asherlc

@Asherlc Asherlc commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add server-authored Body decision context to both the web Body page and mobile Recovery tab.
  • Show latest positive measurement provenance: provider, optional source/device, and recorded clock time in the configured user timezone.
  • Return the Trend Weight contract (10% EWMA step, linear interpolation for gaps, non-positive exclusion, outliers retained).
  • Compute an informational personalized measurement-variation band from the latest 30 scale-to-Trend-Weight residuals after 8 readings using Tukey inner fences, without removing source observations.
  • Share the decision-quality copy and formatting across web and mobile, with focused tests, stories, and methodology documentation.
  • Mask Infisical secret values before writing them to GITHUB_ENV; document the exposed EXPO_TOKEN follow-up.

Validation

  • pnpm test:changed — 15,385 passed, 21 skipped across 1,016 files.
  • pnpm typecheck — passed.
  • pnpm lint:sandbox — passed.
  • Infisical masking policy test — passed.
  • pnpm test:integration -- packages/server/src/repositories/body-repository.integration.test.ts — blocked before tests because Docker cannot create the Compose network: all predefined address pools have been fully subnetted.
  • Full pnpm lint reached lint:analytics-sql but was blocked by ClickHouse unavailable at 127.0.0.1:8123.

Security follow-up

The historical EXPO_TOKEN value must be revoked in Expo and replaced in Infisical for dev/prod/staging. Expo documents revocation through the Access Tokens dashboard: https://docs.expo.dev/accounts/programmatic-access/#revoke-access-tokens.

Fixes #2145

Summary by Sourcery

Introduce a server-authored body measurement decision context, including latest positive measurement provenance, trend-weight contract, and personalized variation band, and surface it across web and mobile experiences while tightening Infisical secret masking in CI.

New Features:

  • Expose a body decision context from the server that combines latest positive measurement provenance, Trend Weight metadata, and an informational personalized measurement-variation band.
  • Return the body decision context in body analytics and mobile recovery APIs and wire it through to the web Body page and mobile Recovery tab UI.
  • Add shared formatting utilities and cross-platform BodyDecisionContext components to present measurement provenance, trend-weight explanation, and variation guidance consistently on web and mobile.

Bug Fixes:

  • Ensure Infisical-provided GitHub Actions secrets are masked before being written to GITHUB_ENV to prevent credential exposure in CI logs.

Enhancements:

  • Extend ClickHouse body measurement queries and repository logic to retrieve provider/source provenance with user-local timestamps for decision context.
  • Document the methodology and contracts for body metrics decision context, including EWMA smoothing parameters and Tukey-based variation band behavior.

Build:

  • Export new body decision context formatting utilities from the shared format package for consumption by server and clients.

CI:

  • Update the Infisical secrets composite GitHub Action to mask fetched values line-by-line before exposing them to later workflow steps.

Documentation:

  • Add body-metrics decision context documentation covering measurement provenance, Trend Weight methodology, and personalized variation band semantics.
  • Record a production incident entry describing prior Infisical credential exposure in the Mobile Preview OTA workflow and its remediation.

Tests:

  • Add focused unit, contract, integration, and UI tests for body decision context computation, formatting, and rendering on web and mobile.
  • Add a policy test that enforces masking of Infisical secrets before any writes to GITHUB_ENV in the composite GitHub Action.

Summary by cubic

Adds a server-authored body measurement decision context across web and mobile with clear provenance, Trend Weight method, and a personalized variation band. Fixes #2145; hardens CI secret masking, validates decision-context dates, improves timezone handling, and allows a nullable decision context in mobile contracts.

  • New Features

    • Serve decisionContext from body analytics and mobile recovery APIs via one service and a new ClickHouse provenance query; include latest positive measurement (provider/source, user‑local time), Trend Weight contract (10% EWMA, linear interpolation, exclude non‑positive, retain outliers), and Tukey inner‑fence variation from up to 30 residuals; return null on errors or insufficient data.
    • Add @dofek/format/body-decision-context and cross‑platform BodyDecisionContext components; standardize UTC timestamps, pick one reading per local day, and document methodology and residual thresholds.
    • Mobile/web: show provenance, method, variation, and clear messaging when data is insufficient or unavailable; mobile recovery and web body pages wired end‑to‑end.
  • Bug Fixes

    • Contracts: make decisionContext nullable in mobile recovery output and test it; validate decision‑context date fields.
    • ClickHouse: compute endDate “now” in the configured timezone; improve selection/gating for latest measurement and variation inputs.
    • CI: mask Infisical secrets before any GITHUB_ENV write and on fetch errors; escape and mask multi‑line values line‑by‑line.

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

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added decision context to body-weight Trend Weight views on web and mobile.
    • Displays measurement dates, sources, trend methodology, variation ranges, observation counts, and guidance.
    • Provides clear messaging when data is insufficient or temporarily unavailable.
  • Bug Fixes
    • Improved secret masking in automated workflows.
    • Corrected timezone-aware body-measurement filtering and timestamps.
  • Documentation
    • Added methodology, measurement provenance, and production incident documentation.

Copilot AI review requested due to automatic review settings August 2, 2026 20:15
@cursor

cursor Bot commented Aug 2, 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.

@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

@codereviewbot-ai

codereviewbot-ai Bot commented Aug 2, 2026

Copy link
Copy Markdown

LGTM! The PR introduces BodyDecisionContext cleanly across the format, mobile, and server packages with solid test coverage and type safety.


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

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 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a server-authored body measurement decision context (latest measurement provenance, Trend Weight contract, and personalized variation band) shared between web Body page and mobile Recovery tab, backed by ClickHouse queries and a dedicated service, plus Infisical secret masking improvements and incident documentation.

Sequence diagram for body decision context computation and mobile recovery rendering

sequenceDiagram
  actor MobileUser
  participant RecoveryScreen
  participant loadMobileRecoveryTab
  participant BodyAnalyticsRepository
  participant BodyClickHouseStore
  participant BodyDecisionContextService
  participant BodyDecisionContext

  MobileUser->>RecoveryScreen: Open Recovery tab
  RecoveryScreen->>loadMobileRecoveryTab: loadMobileRecoveryTab(endDate)
  loadMobileRecoveryTab->>BodyAnalyticsRepository: getBodyDecisionContext(endDate)
  BodyAnalyticsRepository->>BodyClickHouseStore: fetchBodyDecisionMeasurements(userId, timezone, endDate)
  BodyAnalyticsRepository->>BodyAnalyticsRepository: getSmoothedWeight(days, endDate)
  BodyAnalyticsRepository->>BodyDecisionContextService: buildBodyDecisionContext(measurements, trendPoints)
  BodyDecisionContextService-->>BodyAnalyticsRepository: BodyDecisionContext
  BodyAnalyticsRepository-->>loadMobileRecoveryTab: decisionContext
  loadMobileRecoveryTab-->>RecoveryScreen: recoveryData with decisionContext
  RecoveryScreen->>BodyDecisionContext: BodyDecisionContext(context)
Loading

File-Level Changes

Change Details Files
Introduce server-side Body decision context modeling and computation from ClickHouse measurements and Trend Weight, exposing it through the body analytics repository/router and mobile recovery service.
  • Define BodyDecisionContext domain model and helpers to select daily measurements and compute Tukey inner-fence residual bands with EWMA configuration constants.
  • Add ClickHouse schema and query to fetch positive body measurements with provider/source and user-local recorded time for decision context provenance.
  • Extend BodyAnalyticsRepository with getBodyDecisionContext that joins provenance rows with existing smoothed weight series and builds the decision context.
  • Update body-analytics router to fetch decision context alongside smoothed weight/prediction/recomposition, handling failures gracefully and adding output schema coverage.
packages/server/src/services/body-decision-context.ts
packages/server/src/services/body-decision-context.test.ts
packages/server/src/repositories/body-clickhouse.ts
packages/server/src/repositories/body-clickhouse.test.ts
packages/server/src/repositories/body-repository.integration.test.ts
packages/server/src/repositories/body-analytics-repository.ts
packages/server/src/repositories/body-analytics-repository.test.ts
packages/server/src/contracts/body-decision-context.ts
packages/server/src/routers/body-analytics.ts
packages/server/src/routers/body-analytics.test.ts
Surface Body decision context in mobile and web UI (Recovery tab and Body page), with shared formatting/copy, stories, and contract tests.
  • Add BodyDecisionContextView formatting utilities and shared strings for Trend Weight method, provenance, variation band, and source guidance in the format package.
  • Implement BodyDecisionContext components for mobile and web that render provenance, method copy, variation explanation, and guidance, using unit providers and provider labels.
  • Thread decisionContext from server responses into mobile recovery and web body page state, adjusting contracts and tests to include the new field.
  • Add Storybook stories and focused component tests for decision context availability/unavailability on both platforms, and update existing page/screen tests to assert the new copy.
packages/format/src/body-decision-context.ts
packages/format/src/body-decision-context.test.ts
packages/format/package.json
packages/mobile/components/BodyDecisionContext.tsx
packages/mobile/components/BodyDecisionContext.test.tsx
packages/mobile/components/BodyDecisionContext.stories.tsx
packages/mobile/app/(tabs)/recovery.tsx
packages/mobile/app/(tabs)/recovery.test.tsx
packages/mobile/app/(tabs)/recovery.stories.tsx
packages/server/src/contracts/mobile-dashboard-contracts.ts
packages/server/src/contracts/mobile-dashboard-contracts.test.ts
packages/server/src/routers/mobile-dashboard.test.ts
packages/server/src/services/mobile-recovery-tab.ts
packages/server/src/services/mobile-recovery-tab.test.ts
packages/web/src/components/BodyDecisionContext.tsx
packages/web/src/components/BodyDecisionContext.test.tsx
packages/web/src/components/BodyDecisionContext.stories.tsx
packages/web/src/pages/BodyPage.tsx
packages/web/src/pages/BodyPage.test.tsx
Document and validate the body metrics decision methodology and Infisical secret masking policy for GitHub Actions.
  • Add body-metrics-decision-context documentation covering measurement provenance, Trend Weight contract, and Tukey-based variation band with references.
  • Update docs index to include the new body metrics methodology document.
  • Fix Infisical composite GitHub Action to mask fetched secrets (including error output) before writing to GITHUB_ENV, and add a focused policy test to guard the ordering.
  • Record a production incident baseline entry describing the EXPO_TOKEN exposure, mitigation, validation, and remaining revocation work.
docs/body-metrics-decision-context.md
docs/README.md
.github/actions/load-infisical-secrets/action.yml
scripts/load-infisical-secrets-policy.test.ts
docs/production-incident-baseline.md

Assessment against linked issues

Issue Objective Addressed Explanation
#2145 Expose measurement provenance for body metrics (device/provider and recorded local time) on the Body page and associated trend surfaces.
#2145 Expose the Trend Weight methodology (smoothing, gap handling, invalid weight handling, outlier policy) and a meaningful-change/variation range in the body metrics UI and API contracts to support trend decisions.
#2145 Document the body metrics decision context methodology (provenance, Trend Weight contract, personalized variation band) for maintainers and auditors.

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

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds server-authored body measurement decision context with provenance, Trend Weight processing rules, and residual variation. It exposes the context through analytics and recovery responses, renders it on web and mobile, adds documentation and tests, and masks Infisical secrets in GitHub Actions.

Changes

Body decision context

Layer / File(s) Summary
Decision context computation and contracts
packages/server/src/services/body-decision-context.ts, packages/server/src/services/body-decision-context.test.ts, packages/server/src/contracts/body-decision-context.ts
The server selects valid daily measurements, computes Tukey residual bands, reports insufficient data, and validates the output shape.
Measurement retrieval and repository assembly
packages/server/src/repositories/body-clickhouse.ts, packages/server/src/repositories/body-analytics-repository.ts, packages/server/src/repositories/*test.ts
ClickHouse returns timezone-aware measurements and provenance. The repository combines them with smoothed trend points.
Shared formatting and client presentation
packages/format/src/body-decision-context.ts, packages/web/src/components/BodyDecisionContext.tsx, packages/mobile/components/BodyDecisionContext.tsx, packages/web/src/pages/BodyPage.tsx, packages/mobile/app/(tabs)/recovery.tsx
Shared helpers format provenance, residuals, and guidance. Web and mobile render available, insufficient-data, and unavailable states.
Analytics and recovery propagation
packages/server/src/routers/body-analytics.ts, packages/server/src/services/mobile-recovery-tab.ts, packages/server/src/contracts/mobile-dashboard-contracts.ts, packages/server/src/services/*test.ts, packages/server/src/routers/*test.ts
Analytics and recovery loads fetch decision context in parallel, return nullable context on failure, and include it in validated payloads.
Fixtures, stories, and documentation
packages/web/src/components/BodyDecisionContext.stories.tsx, packages/mobile/components/BodyDecisionContext.stories.tsx, packages/format/src/body-decision-context.test.ts, docs/body-metrics-decision-context.md, docs/README.md
Fixtures, stories, and tests cover context states. Documentation defines measurement selection, Trend Weight processing, and residual-band behavior.

Infisical secret masking

Layer / File(s) Summary
Secret masking and incident record
.github/actions/load-infisical-secrets/action.yml, docs/production-incident-baseline.md
The action masks escaped multiline secret values before error reporting and GITHUB_ENV writes. The incident record documents the credential exposure and mitigation.

Mobile dashboard test maintenance

Layer / File(s) Summary
Recovery and training test-suite removal
packages/server/src/routers/mobile-dashboard.test.ts
The recovery and training dashboard test suites, fixtures, and imports are removed.

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

Assessment against linked issues

Objective Addressed Explanation
Show device/provider and measurement source context for body trend decisions [#2145]
Show time of day and measurement provenance [#2145]
Show smoothing, interpolation, invalid-value, and outlier policy [#2145]
Show a meaningful-change range [#2145]

Out-of-scope changes

Code Change Explanation
Mask Infisical secrets and document an EXPO_TOKEN incident (.github/actions/load-infisical-secrets/action.yml:107-131, docs/production-incident-baseline.md:162-209) This addresses CI credential exposure, not body measurement decision context.
Remove recovery and training dashboard test suites (packages/server/src/routers/mobile-dashboard.test.ts:166-169,784) The linked objective does not require removing mobile dashboard regression coverage.

Sequence Diagram(s)

sequenceDiagram
  participant WebOrMobile
  participant AnalyticsOrRecovery
  participant BodyAnalyticsRepository
  participant ClickHouse
  participant BodyDecisionContext
  WebOrMobile->>AnalyticsOrRecovery: request body overview or recovery data
  AnalyticsOrRecovery->>BodyAnalyticsRepository: getBodyDecisionContext(endDate)
  BodyAnalyticsRepository->>ClickHouse: fetch measurements and trend points
  ClickHouse-->>BodyAnalyticsRepository: return provenance and trend data
  BodyAnalyticsRepository->>BodyDecisionContext: build decision context
  BodyDecisionContext-->>AnalyticsOrRecovery: return nullable context
  AnalyticsOrRecovery-->>WebOrMobile: return body data and decision context
Loading

Possibly related PRs

  • Asherlc/dofek#1110: Changes the body analytics repository and body-measurement retrieval used by this PR.
  • Asherlc/dofek#1579: Changes the ClickHouse body-measurement view consumed by fetchBodyDecisionMeasurements.
  • Asherlc/dofek#2301: Adds server-authored decision-context data with shared web and mobile presentation patterns.

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

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is imperative, under 70 characters, and descriptive, but it does not use the required area prefix format such as [server], [web], or [mobile]. Replace feat(body): with an area prefix, for example [server] Add measurement decision context, and keep the title under 70 characters without punctuation.
✅ 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

Copy link
Copy Markdown

PR Summary by Qodo

Body: add server-authored measurement decision context (web + mobile)

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add server-authored body decision context: provenance, Trend Weight contract, and variation band.
• Render shared decision-quality copy on web Body and mobile Recovery experiences.
• Mask Infisical secrets before writing to GITHUB_ENV and add regression policy test.
Diagram

graph TD
  W["Web Body page"] --> R["Server body analytics API"] --> BR["Body analytics repo"] --> DCS["Decision context service"]
  M["Mobile Recovery tab"] --> MS["Mobile recovery service"] --> BR
  W --> F["@dofek/format copy/formatters"]
  M --> F
  BR --> CH[("ClickHouse body view")]
  subgraph Legend
    direction LR
    _ui["UI surface"] ~~~ _api["API/service"] ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Compute variation band in ClickHouse SQL
  • ➕ Less application logic and smaller payloads (only band numbers returned)
  • ➕ Centralizes statistical computation next to the underlying measurement view
  • ➖ Harder to unit test and iterate on quantile/fence logic
  • ➖ ClickHouse SQL quantile semantics and edge cases can be more opaque
2. Client-side variation computation (web/mobile)
  • ➕ Avoids new server endpoint/contract fields
  • ➕ UI can experiment quickly without server deploy
  • ➖ Duplicates business logic across platforms and risks divergence
  • ➖ Requires shipping more raw data and increases client complexity

Recommendation: Keep the current approach (server-authored decision context + shared formatting package). It ensures a single source of truth for provenance selection, Trend Weight contract messaging, and variation-band methodology while still allowing both web and mobile to render identical copy with minimal per-client logic.

Files changed (34) +1337 / -30

Enhancement (12) +473 / -26
body-decision-context.tsIntroduce shared decision context copy and formatting helpers +86/-0

Introduce shared decision context copy and formatting helpers

• Defines shared copy constants and a view interface for UI rendering. Adds formatting helpers for provenance display, signed residual formatting, and variation-band messaging with explicit insufficient-data behavior.

packages/format/src/body-decision-context.ts

recovery.tsxRender BodyDecisionContext component in the Recovery tab +2/-8

Render BodyDecisionContext component in the Recovery tab

• Removes inline Trend Weight explanation text and styles. Inserts the shared BodyDecisionContext component below the weight card using server-provided decisionContext when available.

packages/mobile/app/(tabs)/recovery.tsx

BodyDecisionContext.tsxIntroduce shared decision context UI component (mobile) +65/-0

Introduce shared decision context UI component (mobile)

• Adds a dedicated component that formats and renders provenance and variation using @dofek/format helpers. Uses unit conversion and provider labels, and falls back to an explicit unavailable state.

packages/mobile/components/BodyDecisionContext.tsx

body-decision-context.tsAdd Zod contract for body decision context API output +33/-0

Add Zod contract for body decision context API output

• Defines the server output schema and TypeScript type for provenance, Trend Weight contract fields, and variation band fields (including method and outliersIncluded).

packages/server/src/contracts/body-decision-context.ts

mobile-dashboard-contracts.tsAdd decisionContext to mobile recovery tab output schema +2/-0

Add decisionContext to mobile recovery tab output schema

• Imports the new decision context schema and adds a nullable decisionContext field to the mobileRecoveryTabOutputSchema.

packages/server/src/contracts/mobile-dashboard-contracts.ts

body-analytics-repository.tsFetch and build body decision context in analytics repository +34/-1

Fetch and build body decision context in analytics repository

• Adds getBodyDecisionContext() that pulls smoothed trend points and provenance rows in parallel. Maps ClickHouse rows into domain objects and delegates computation to buildBodyDecisionContext().

packages/server/src/repositories/body-analytics-repository.ts

body-clickhouse.tsAdd ClickHouse query for decision measurement provenance rows +39/-0

Add ClickHouse query for decision measurement provenance rows

• Introduces a new schema and query to retrieve positive weight measurements with provider_id, source_name, and recorded_at_local derived from the configured timezone. Orders by local day and timestamp for stable daily selection.

packages/server/src/repositories/body-clickhouse.ts

body-analytics.tsReturn decisionContext from body analytics weight overview endpoint +9/-1

Return decisionContext from body analytics weight overview endpoint

• Extends the weightOverview procedure to fetch decision context alongside existing data via Promise.allSettled. Captures exceptions on decision context failures and returns decisionContext as nullable in the output schema.

packages/server/src/routers/body-analytics.ts

body-decision-context.tsImplement server-side decision context builder (provenance + variation) +131/-0

Implement server-side decision context builder (provenance + variation)

• Introduces deterministic daily measurement selection (positive-only, earliest-in-day) and computes a residual band over the latest 30 residuals using quartiles and inner fences. Returns explicit Trend Weight contract metadata and preserves outliers in underlying observations.

packages/server/src/services/body-decision-context.ts

mobile-recovery-tab.tsFetch body decision context for mobile Recovery tab responses +19/-15

Fetch body decision context for mobile Recovery tab responses

• Extends the Recovery tab loader to call bodyRepo.getBodyDecisionContext(endDate) in parallel with other queries. Returns the decisionContext in the response payload.

packages/server/src/services/mobile-recovery-tab.ts

BodyDecisionContext.tsxIntroduce shared decision context UI component (web) +49/-0

Introduce shared decision context UI component (web)

• Adds a web component that renders the same decision-quality copy as mobile, using shared @dofek/format formatting and web unit context conversion.

packages/web/src/components/BodyDecisionContext.tsx

BodyPage.tsxUse shared Trend Weight copy and render decision context on Body page +4/-1

Use shared Trend Weight copy and render decision context on Body page

• Replaces the tooltip Trend Weight description with the shared contract copy constant. Renders the BodyDecisionContext component beneath the Trend Weight chart using the server-provided decisionContext.

packages/web/src/pages/BodyPage.tsx

Bug fix (1) +10 / -0
action.ymlMask Infisical secret values before exporting to GITHUB_ENV +10/-0

Mask Infisical secret values before exporting to GITHUB_ENV

• Adds a masking helper that registers each fetched secret value (line-by-line for multiline secrets) via GitHub Actions add-mask. Ensures masking occurs on both fetch failure output and successful secret export.

.github/actions/load-infisical-secrets/action.yml

Tests (14) +588 / -4
body-decision-context.test.tsAdd unit tests for decision context copy and formatting +74/-0

Add unit tests for decision context copy and formatting

• Verifies provenance formatting, residual-band messaging for available/insufficient states, and that the shared Trend Weight/source guidance copy remains consistent.

packages/format/src/body-decision-context.test.ts

recovery.test.tsxUpdate Recovery screen expectations for missing decision context +3/-1

Update Recovery screen expectations for missing decision context

• Replaces the previous hardcoded Trend Weight explanation assertion with the shared 'decision context unavailable' copy when context is null.

packages/mobile/app/(tabs)/recovery.test.tsx

BodyDecisionContext.test.tsxAdd component tests for shared decision copy on mobile +58/-0

Add component tests for shared decision copy on mobile

• Validates that mobile renders the same provenance/method/variation/source guidance copy as web, and shows the unavailable message when context is null.

packages/mobile/components/BodyDecisionContext.test.tsx

mobile-dashboard-contracts.test.tsExtend recovery fixture contract tests with decisionContext +27/-0

Extend recovery fixture contract tests with decisionContext

• Updates the valid recovery fixture to include a decisionContext object matching the new schema, ensuring contract validation stays up to date.

packages/server/src/contracts/mobile-dashboard-contracts.test.ts

body-analytics-repository.test.tsTest repository builds decision context from trend + provenance +26/-0

Test repository builds decision context from trend + provenance

• Adds a focused test verifying latest measurement provenance selection and that variation becomes available at the minimum observation threshold.

packages/server/src/repositories/body-analytics-repository.test.ts

body-clickhouse.test.tsAdd unit test for decision measurement ClickHouse query +40/-0

Add unit test for decision measurement ClickHouse query

• Verifies the new ClickHouse query returns provider/source/local timestamp fields and filters to positive weights from analytics.v_body_measurement.

packages/server/src/repositories/body-clickhouse.test.ts

body-repository.integration.test.tsIntegration test for provider/source provenance and local clock time +41/-1

Integration test for provider/source provenance and local clock time

• Adds an integration test that inserts a sample measurement, rebuilds the view, and asserts fetchBodyDecisionMeasurements returns correct provider/source and timezone-adjusted recorded_at_local.

packages/server/src/repositories/body-repository.integration.test.ts

body-analytics.test.tsAdd router test coverage for decisionContext in weightOverview +60/-2

Add router test coverage for decisionContext in weightOverview

• Adjusts mocked sensor store rows to include provenance fields and adds a test ensuring decisionContext is returned with expected provenance and variation values.

packages/server/src/routers/body-analytics.test.ts

mobile-dashboard.test.tsUpdate mobile dashboard recovery tests for new decisionContext field +2/-0

Update mobile dashboard recovery tests for new decisionContext field

• Extends recovery response expectations to include decisionContext: null in relevant test cases.

packages/server/src/routers/mobile-dashboard.test.ts

body-decision-context.test.tsAdd unit tests for decision context builder methodology +110/-0

Add unit tests for decision context builder methodology

• Covers latest-provenance selection, residual-band computation using Tukey inner fences with the last 30 residuals, insufficient-data behavior below 8 readings, and the explicit Trend Weight handling contract.

packages/server/src/services/body-decision-context.test.ts

mobile-recovery-tab.test.tsTest mobile Recovery tab includes body decision context +41/-0

Test mobile Recovery tab includes body decision context

• Adds a test asserting loadMobileRecoveryTab returns decisionContext alongside other recovery data and mocks the repository call.

packages/server/src/services/mobile-recovery-tab.test.ts

BodyDecisionContext.test.tsxAdd component tests for shared decision copy on web +56/-0

Add component tests for shared decision copy on web

• Validates provenance, Trend Weight method copy, variation messaging, and source guidance rendering, plus the null-context unavailable state.

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

BodyPage.test.tsxUpdate BodyPage test fixture to include decisionContext +27/-0

Update BodyPage test fixture to include decisionContext

• Extends the healthyWeightOverview mock response with a decisionContext object to match the updated weightOverview payload shape.

packages/web/src/pages/BodyPage.test.tsx

load-infisical-secrets-policy.test.tsAdd policy test ensuring secret masking precedes GITHUB_ENV writes +23/-0

Add policy test ensuring secret masking precedes GITHUB_ENV writes

• Adds a Vitest contract test that parses the composite action YAML and asserts add-mask invocation occurs before any GITHUB_ENV write, including the multiline masking loop.

scripts/load-infisical-secrets-policy.test.ts

Documentation (3) +89 / -0
README.mdLink new body metrics decision context documentation +1/-0

Link new body metrics decision context documentation

• Adds the new methodology/provenance documentation page to the docs index table.

docs/README.md

body-metrics-decision-context.mdDocument provenance, Trend Weight contract, and variation band methodology +38/-0

Document provenance, Trend Weight contract, and variation band methodology

• Introduces a concise explanation of what the decision context is and how it is computed and displayed. Documents the Trend Weight EWMA contract and the Tukey inner-fence residual band calculation including insufficient-data behavior.

docs/body-metrics-decision-context.md

production-incident-baseline.mdRecord incident: unmasked Infisical credential in Mobile Preview OTA workflow +50/-0

Record incident: unmasked Infisical credential in Mobile Preview OTA workflow

• Adds an incident entry describing the exposure risk, root cause (missing add-mask before GITHUB_ENV write), validation, and required Expo token revocation/replacement follow-up.

docs/production-incident-baseline.md

Other (4) +177 / -0
package.jsonExport shared body decision context formatter module +1/-0

Export shared body decision context formatter module

• Adds a new export entry for the body decision context formatting/copy utilities so web and mobile can share them.

packages/format/package.json

recovery.stories.tsxSeed Recovery stories with decisionContext fixture data +27/-0

Seed Recovery stories with decisionContext fixture data

• Extends the seeded recovery provider fixture to include a populated decisionContext block (provenance, trend contract, and variation).

packages/mobile/app/(tabs)/recovery.stories.tsx

BodyDecisionContext.stories.tsxAdd Storybook stories for BodyDecisionContext (mobile) +91/-0

Add Storybook stories for BodyDecisionContext (mobile)

• Introduces stories covering available and insufficient-data contexts. Provides a minimal mocked settings tRPC link for unit system configuration in Storybook.

packages/mobile/components/BodyDecisionContext.stories.tsx

BodyDecisionContext.stories.tsxAdd Storybook stories for BodyDecisionContext (web) +58/-0

Add Storybook stories for BodyDecisionContext (web)

• Introduces web stories for available and insufficient-data states, wrapping the component with a metric UnitContext decorator.

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

@qodo-code-review

qodo-code-review Bot commented Aug 2, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 211 rules

Grey Divider


Action required

1. Access-window clause breaks query ✓ Resolved 🐞 Bug ≡ Correctness
Description
fetchBodyDecisionMeasurements appends accessWindowDateClause(), which emits predicates on a
local_date column that analytics.v_body_measurement does not expose, so restricted-access requests
will fail at runtime when an AccessWindow is present. This causes decisionContext fetches to reject
(and currently hard-fails mobile recovery).
Code

packages/server/src/repositories/body-clickhouse.ts[R188-191]

+        AND weight_kg > 0
+        AND ${localDateExpression} <= ${endDateExpression(endDate)}
+        ${accessWindowDateClause(accessWindow)}
+      ORDER BY ${localDateExpression} ASC, recorded_at ASC
Relevance

●●● Strong

Invalid SQL from referencing non-existent view columns is typically fixed; similar runtime
query-column mismatch issues were accepted.

PR-#2317

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The shared access-window helper hardcodes local_date, but the ClickHouse view used in the new
decision-context query does not include that column, so the generated SQL is invalid whenever
AccessWindow filtering is enabled.

packages/server/src/repositories/body-clickhouse.ts[88-99]
packages/server/src/repositories/body-clickhouse.ts[167-195]
src/db/clickhouse-migrations/0058_migrate_body_measurement_to_dbt.ts[88-111]
packages/server/src/repositories/body-analytics-repository.ts[271-294]
packages/server/src/services/mobile-recovery-tab.ts[227-245]

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

### Issue description
`fetchBodyDecisionMeasurements()` applies `accessWindowDateClause(accessWindow)` to a query that reads from `analytics.v_body_measurement`. That helper expands to `AND local_date >= ... AND local_date < ...`, but `analytics.v_body_measurement` (the ClickHouse serving view) does not have a `local_date` column, so the SQL is invalid for non-`full` access windows.

### Issue Context
- `accessWindowDateClause()` is currently designed for queries that already expose a `local_date` column/alias.
- The decision-context query computes a local-date expression inline (`toDate(toTimeZone(recorded_at, {timezone:String}))`) but never aliases it to `local_date`.

### Fix Focus Areas
- packages/server/src/repositories/body-clickhouse.ts[84-195]
- src/db/clickhouse-migrations/0058_migrate_body_measurement_to_dbt.ts[88-111]

### Implementation notes
Choose one:
1) Replace the access-window clause usage in this query with explicit bounds on the computed local-date expression, e.g.:
```sql
AND toDate(toTimeZone(recorded_at, {timezone:String})) >= toDate({accessStart:String})
AND toDate(toTimeZone(recorded_at, {timezone:String})) < toDate({accessEnd:String})
```
2) Or define/alias `local_date` in the query (e.g., via `WITH ... AS local_date`) and keep using `accessWindowDateClause()`.

Add/adjust tests for the restricted-access-window path to prevent regressions.

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


2. Recovery cache version stale ✓ Resolved 🐞 Bug ☼ Reliability
Description
mobileDashboard.recovery and bodyAnalytics.weightOverview cache responses using
HEALTH_STATUS_CACHE_KEY_VERSION, but their output schemas now require a new decisionContext
field (nullable but not optional). If caches persist across deploys without a keyVersion bump,
pre-deploy cached payloads missing decisionContext can be served under the same key and fail Zod
output parsing/validation until TTL expiry.
Code

packages/server/src/contracts/mobile-dashboard-contracts.ts[R178-181]

      interpolated: z.boolean(),
    }),
  ),
+  decisionContext: bodyDecisionContextOutputSchema.nullable(),
Relevance

●●● Strong

Team routinely bumps cache keyVersion when adding required output fields to cached procedures to
avoid stale schema failures.

PR-#2407
PR-#2384
PR-#2322

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cited router procedures use cachedProtectedQuery, which builds cache keys using
policy.keyVersion, and the cache layer returns cached hits directly when the key matches; because
the updated output schemas now require decisionContext to exist on the response object (even
though it is .nullable()), any cached entries created before the field existed will not match the
new required shape. Without changing (bumping) the keyVersion (either per-endpoint or via
HEALTH_STATUS_CACHE_KEY_VERSION), the system may continue serving these incompatible old payloads
after deploy, leading to output parsing/validation failures until the cached entries expire.

packages/server/src/contracts/mobile-dashboard-contracts.ts[171-185]
packages/server/src/routers/mobile-dashboard.ts[172-196]
packages/server/src/trpc.ts[284-317]
packages/server/src/routers/body-analytics.ts[105-173]
packages/server/src/services/health-status.ts[16-17]
PR-#2407

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 output schemas for `mobileDashboard.recovery` and `bodyAnalytics.weightOverview` now include a required `decisionContext` field (nullable but not optional), but both endpoints continue to use a stable cache `keyVersion` based on `HEALTH_STATUS_CACHE_KEY_VERSION`. This can cause pre-deploy cached payloads (missing `decisionContext`) to be returned on cache hit after deploy and fail Zod output parsing/validation until TTL expiry.

## Issue Context
- `cachedProtectedQuery` builds the cache key using `policy.keyVersion`, and cache hits are returned directly when the key matches.
- `decisionContext: ... .nullable()` is still required to be present on the object; old cached payloads may omit it entirely.
- Fix can be localized by bumping the `keyVersion` per procedure (recommended to avoid unnecessary cache invalidation), or broadened by bumping `HEALTH_STATUS_CACHE_KEY_VERSION` to invalidate all caches sharing that version.

## Fix Focus Areas
- packages/server/src/contracts/mobile-dashboard-contracts.ts[151-205]
- packages/server/src/routers/mobile-dashboard.ts[172-196]
- packages/server/src/routers/body-analytics.ts[105-173]
- packages/server/src/trpc.ts[284-317]
- packages/server/src/services/health-status.ts[16-17]

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



Remediation recommended

3. captureException() missing context ✓ Resolved 📘 Rule violation ◔ Observability
Description
The new error reporting for decisionContextResult captures the exception without any contextual
tags/extras (e.g., user/procedure/endDate), reducing triage value in Sentry. This violates the
requirement to include available context when reporting internal failures.
Code

packages/server/src/routers/body-analytics.ts[R135-136]

+      if (decisionContextResult.status === "rejected") {
+        captureException(decisionContextResult.reason);
Relevance

●●● Strong

Repo precedent favors captureException with tags/extras/context for triage rather than error-only
reports.

PR-#2351
PR-#746
PR-#1679

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 721936 requires reporting internal failures to Sentry with sufficient context when
available. In body-analytics.ts, the new rejection path calls captureException with only the
error, even though ctx.userId and input.endDate are available in the same function scope;
captureException also supports an optional context argument.

Rule 721936: Report internal failures to Sentry with sufficient context
packages/server/src/routers/body-analytics.ts[108-118]
packages/server/src/routers/body-analytics.ts[135-137]
src/lib/error-reporting.ts[55-61]

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 `captureException(decisionContextResult.reason)` call reports an internal failure without attaching available context (procedure name, `ctx.userId`, `input.endDate`, etc.), making the alert harder to investigate.

## Issue Context
`captureException` supports an optional `captureContext` argument; in this code path, useful context is available in scope and should be attached.

## Fix Focus Areas
- packages/server/src/routers/body-analytics.ts[108-137]
- src/lib/error-reporting.ts[55-61]

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


4. mobile-recovery-tab.test.ts over 1000 lines ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
packages/server/src/services/mobile-recovery-tab.test.ts is 1180 lines long, exceeding the
1000-line maximum for TypeScript files. This makes the test suite harder to maintain and violates
the file-size compliance limit.
Code

packages/server/src/services/mobile-recovery-tab.test.ts[10]

+import type { BodyDecisionContext } from "./body-decision-context.ts";
Relevance

●●● Strong

1000-line TS file compliance findings are commonly addressed by extracting tests/helpers into
smaller files.

PR-#2316

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 773521 requires changed TypeScript files to be at most 1000 lines. The PR modifies
mobile-recovery-tab.test.ts (see diff pointer), and the file contains line numbers beyond 1000
(e.g., content at line 1001), demonstrating it exceeds the limit.

Rule 773521: Enforce a 1000-line maximum per TypeScript file
packages/server/src/services/mobile-recovery-tab.test.ts[1001-1010]

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

## Issue description
`packages/server/src/services/mobile-recovery-tab.test.ts` exceeds the 1000-line maximum, violating the TypeScript file size limit.

## Issue Context
This PR adds/updates tests in `mobile-recovery-tab.test.ts`, which is already above the maximum allowed size (1180 lines total). The suite should be split into smaller, responsibility-focused test files.

## Fix Focus Areas
- packages/server/src/services/mobile-recovery-tab.test.ts[1-1180]

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


5. mobile-dashboard.test.ts over 1000 lines ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
packages/server/src/routers/mobile-dashboard.test.ts is 1252 lines long, exceeding the 1000-line
maximum for TypeScript files. This violates the maintainability constraint requiring smaller TS
files.
Code

packages/server/src/routers/mobile-dashboard.test.ts[901]

+      decisionContext: null,
Relevance

●●● Strong

1000-line TS file limit has been enforced before; large test files were split/extracted to comply.

PR-#2316

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 773521 sets a 1000-line maximum for TypeScript files. The PR modifies
mobile-dashboard.test.ts (see diff pointer), and the file contains content at line 1001, proving
it exceeds the limit.

Rule 773521: Enforce a 1000-line maximum per TypeScript file
packages/server/src/routers/mobile-dashboard.test.ts[1001-1005]

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

## Issue description
`packages/server/src/routers/mobile-dashboard.test.ts` exceeds the 1000-line maximum, violating the TypeScript file size limit.

## Issue Context
This PR adds/updates assertions in `mobile-dashboard.test.ts`, and the file currently has 1252 lines. Split the tests into smaller files (e.g., per router procedure such as `mobile-dashboard.recovery.test.ts`, etc.) so each file stays under 1000 lines.

## Fix Focus Areas
- packages/server/src/routers/mobile-dashboard.test.ts[1-1252]

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



Informational

6. Recovery endpoint fails on context ✓ Resolved 🐞 Bug ☼ Reliability
Description
loadMobileRecoveryTab uses Promise.all including bodyRepo.getBodyDecisionContext(endDate), so any
decision-context rejection rejects the entire Recovery response instead of returning
decisionContext:null. This prevents the client from rendering its intended "context unavailable" UI
and increases the blast radius of ClickHouse/query issues.
Code

packages/server/src/services/mobile-recovery-tab.ts[R240-243]

+        healthspanWeeks * 7,
+      ),
+      bodyRepo.getBodyDecisionContext(endDate),
+    ],
Relevance

● Weak

Prior similar request to replace Promise.all with allSettled/guard partial failures in service was
explicitly rejected.

PR-#1572

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The recovery service now uses Promise.all with the new decisionContext fetch, making it a hard
dependency; the body analytics router demonstrates the intended behavior by treating decisionContext
failures as non-fatal.

packages/server/src/services/mobile-recovery-tab.ts[227-245]
packages/server/src/routers/body-analytics.ts[105-146]

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

### Issue description
`loadMobileRecoveryTab()` now fetches decision context inside a single `Promise.all([...])`. If `getBodyDecisionContext()` rejects (e.g., ClickHouse error, access-window SQL issue), the entire recovery tab request rejects and the app cannot fall back to `decisionContext: null`.

### Issue Context
- The UI explicitly supports `context == null` ("Measurement decision context is temporarily unavailable...").
- `bodyAnalytics.weightOverview` already treats decisionContext as optional by using `Promise.allSettled` and returning `null` on failure.

### Fix Focus Areas
- packages/server/src/services/mobile-recovery-tab.ts[223-286]
- packages/server/src/routers/body-analytics.ts[105-146]

### Implementation notes
Wrap only the decisionContext fetch with `allSettled`/`catch`:
- Option A: `const decisionContext = await bodyRepo.getBodyDecisionContext(endDate).catch((err) => { captureException(err); return null; });`
- Option B: change the whole block to `Promise.allSettled` and keep required vs optional semantics (required ones still throw).

Add a unit test that forces `getBodyDecisionContext` to reject and asserts the recovery payload still returns with `decisionContext: null`.

ⓘ 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 packages/server/src/routers/body-analytics.ts Outdated
Comment thread packages/server/src/services/mobile-recovery-tab.test.ts
Comment thread packages/server/src/routers/mobile-dashboard.test.ts
Comment thread packages/server/src/repositories/body-clickhouse.ts Outdated
Comment thread packages/server/src/contracts/mobile-dashboard-contracts.ts

@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: 9

🤖 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 @.github/actions/load-infisical-secrets/action.yml:
- Around line 107-116: Update mask_value to escape secret values for GitHub
workflow commands by replacing percent signs before escaping carriage returns
and line feeds, then emit each add-mask command with printf '%s\n' rather than
echo. Apply the same escaping and safe command emission to the success path that
masks retrieved values before output.

In `@docs/production-incident-baseline.md`:
- Around line 26-27: Update the wrapped Markdown line in the incident baseline
text so the literal PR reference beginning with `#2303` cannot be interpreted as a
malformed heading, while preserving the existing wording and meaning.

In `@packages/format/src/body-decision-context.ts`:
- Around line 10-41: Update BodyDecisionContextView to reference the named
BodyDecisionLatestMeasurement and BodyDecisionVariation interfaces for
latestMeasurement and variation instead of duplicating their inline shapes. Move
those interface declarations above BodyDecisionContextView for clarity,
preserving the existing nullable latestMeasurement contract.

In `@packages/mobile/components/BodyDecisionContext.stories.tsx`:
- Around line 56-91: Add an Unavailable story alongside Available and
InsufficientData in the story module, configuring its args with context set to
null so the component’s unavailable-context path is covered.

In `@packages/server/src/repositories/body-clickhouse.ts`:
- Around line 58-65: Update the ClickHouse query fields producing recorded_at,
including the additional occurrence around the body decision measurement query,
to format timestamps explicitly in UTC as an ISO-style string with a trailing Z
before schema parsing. Keep the recorded_at field mapped to
timestampStringSchema and leave unrelated fields unchanged.

In `@packages/server/src/services/mobile-recovery-tab.ts`:
- Around line 227-244: Wrap the getBodyDecisionContext call in
loadMobileRecoveryTab’s Promise.all with a per-call catch that logs the
rejection according to the mandatory catch-block rule and returns null. Preserve
resolution of the remaining recovery-tab data, and add a regression test in
mobile-recovery-tab.test.ts asserting a rejected decision-context fetch yields
result.decisionContext as null while the other payload fields resolve.

In `@packages/web/src/components/BodyDecisionContext.stories.tsx`:
- Around line 44-58: Add an Unavailable story alongside InsufficientData in the
BodyDecisionContext story definitions, passing context: null through its args to
cover the component’s unavailable state while preserving the existing meaningful
and insufficient-data stories.

In `@packages/web/src/components/BodyDecisionContext.test.tsx`:
- Around line 7-33: Update the context fixture declaration in
BodyDecisionContext.test.tsx to use satisfies against the BodyDecisionContext
prop contract, removing the individual as const assertions from trendWeight and
variation literals. Preserve the fixture values while allowing TypeScript to
validate them against the component’s expected context type.

In `@scripts/load-infisical-secrets-policy.test.ts`:
- Around line 8-21: Remove the static action.yml inspection test, including the
test case that reads actionPath and asserts mask-loop, masking-command, and
ordering strings. Use approved schema, build, lint, or review validation
instead, and update the incident document’s validation section to describe the
checks that remain; do not replace it with another test for declarative
configuration.
🪄 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: d976a7e8-c56c-4af3-9eb5-c9ac3d9887e8

📥 Commits

Reviewing files that changed from the base of the PR and between f49cc12 and 18f9a17.

📒 Files selected for processing (34)
  • .github/actions/load-infisical-secrets/action.yml
  • docs/README.md
  • docs/body-metrics-decision-context.md
  • docs/production-incident-baseline.md
  • packages/format/package.json
  • packages/format/src/body-decision-context.test.ts
  • packages/format/src/body-decision-context.ts
  • packages/mobile/app/(tabs)/recovery.stories.tsx
  • packages/mobile/app/(tabs)/recovery.test.tsx
  • packages/mobile/app/(tabs)/recovery.tsx
  • packages/mobile/components/BodyDecisionContext.stories.tsx
  • packages/mobile/components/BodyDecisionContext.test.tsx
  • packages/mobile/components/BodyDecisionContext.tsx
  • packages/server/src/contracts/body-decision-context.ts
  • packages/server/src/contracts/mobile-dashboard-contracts.test.ts
  • packages/server/src/contracts/mobile-dashboard-contracts.ts
  • packages/server/src/repositories/body-analytics-repository.test.ts
  • packages/server/src/repositories/body-analytics-repository.ts
  • packages/server/src/repositories/body-clickhouse.test.ts
  • packages/server/src/repositories/body-clickhouse.ts
  • packages/server/src/repositories/body-repository.integration.test.ts
  • packages/server/src/routers/body-analytics.test.ts
  • packages/server/src/routers/body-analytics.ts
  • packages/server/src/routers/mobile-dashboard.test.ts
  • packages/server/src/services/body-decision-context.test.ts
  • packages/server/src/services/body-decision-context.ts
  • packages/server/src/services/mobile-recovery-tab.test.ts
  • packages/server/src/services/mobile-recovery-tab.ts
  • packages/web/src/components/BodyDecisionContext.stories.tsx
  • packages/web/src/components/BodyDecisionContext.test.tsx
  • packages/web/src/components/BodyDecisionContext.tsx
  • packages/web/src/pages/BodyPage.test.tsx
  • packages/web/src/pages/BodyPage.tsx
  • scripts/load-infisical-secrets-policy.test.ts

Comment thread .github/actions/load-infisical-secrets/action.yml
Comment thread docs/production-incident-baseline.md Outdated
Comment thread packages/format/src/body-decision-context.ts Outdated
Comment thread packages/mobile/components/BodyDecisionContext.stories.tsx
Comment thread packages/server/src/repositories/body-clickhouse.ts
Comment thread packages/server/src/services/mobile-recovery-tab.ts
Comment thread packages/web/src/components/BodyDecisionContext.stories.tsx
Comment thread packages/web/src/components/BodyDecisionContext.test.tsx Outdated
Comment thread scripts/load-infisical-secrets-policy.test.ts Outdated
@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.

1 similar comment
@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: 5

🤖 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 `@packages/server/src/repositories/body-clickhouse.ts`:
- Line 193: Update fetchBodyDecisionMeasurements and its
accessWindowDateClause/endDateExpression handling so endDate === "now" resolves
to the requested user-local date via the existing timezone expression, matching
recorded_at comparisons instead of using session-dependent today(). Add boundary
coverage for measurements on the current user-local day when the ClickHouse
session timezone differs.

In `@packages/server/src/routers/mobile-dashboard.recovery-training.test.ts`:
- Around line 149-153: Consolidate the recovery and training scenarios from this
file into mobile-dashboard.test.ts so mobileDashboardRouter has a single unit
test file. Remove the separate mobile-dashboard.recovery-training.test.ts file
and preserve its existing coverage in the consolidated test suite.
- Around line 78-110: Parameterize makeSensorStore with an optional custom query
function while preserving its current default query behavior. Replace the
duplicated inline ActivitySensorStore objects in the consolidated recovery and
training tab tests with makeSensorStore calls that supply their custom query
functions, reusing the shared mock methods and allowing the existing
test-specific query behavior to remain unchanged.

In `@packages/server/src/services/mobile-recovery-tab.context.test.ts`:
- Around line 22-134: Move the two `loadMobileRecoveryTab` tests from
`mobile-recovery-tab.context.test.ts` into the existing
`mobile-recovery-tab.test.ts` suite, preserving their assertions and mocks.
Remove the separate scenario test file or its duplicated coverage, and keep
production responsibilities unchanged.

In `@packages/server/src/services/mobile-recovery-tab.test-helpers.ts`:
- Around line 1-8: Rename the shared helper module from
mobile-recovery-tab.test-helpers.ts to test-helpers.ts, then update every import
and reference to use the new colocated module name. Preserve all existing
fixtures, mocks, and exported symbols without other changes.
🪄 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: ea572b81-f5ab-4302-8404-45aef63d0056

📥 Commits

Reviewing files that changed from the base of the PR and between 18f9a17 and fdab846.

📒 Files selected for processing (17)
  • .github/actions/load-infisical-secrets/action.yml
  • docs/production-incident-baseline.md
  • packages/format/src/body-decision-context.ts
  • packages/mobile/components/BodyDecisionContext.stories.tsx
  • packages/server/src/repositories/body-clickhouse.test.ts
  • packages/server/src/repositories/body-clickhouse.ts
  • packages/server/src/routers/body-analytics.test.ts
  • packages/server/src/routers/body-analytics.ts
  • packages/server/src/routers/mobile-dashboard.recovery-training.test.ts
  • packages/server/src/routers/mobile-dashboard.test.ts
  • packages/server/src/services/health-status.ts
  • packages/server/src/services/mobile-recovery-tab.context.test.ts
  • packages/server/src/services/mobile-recovery-tab.test-helpers.ts
  • packages/server/src/services/mobile-recovery-tab.test.ts
  • packages/server/src/services/mobile-recovery-tab.ts
  • packages/web/src/components/BodyDecisionContext.stories.tsx
  • packages/web/src/components/BodyDecisionContext.test.tsx
💤 Files with no reviewable changes (1)
  • packages/server/src/routers/mobile-dashboard.test.ts

Comment thread packages/server/src/repositories/body-clickhouse.ts
Comment thread packages/server/src/routers/mobile-dashboard.recovery-training.test.ts Outdated
Comment thread packages/server/src/routers/mobile-dashboard.recovery-training.test.ts Outdated
Comment thread packages/server/src/services/mobile-recovery-tab.context.test.ts Outdated
Comment thread packages/server/src/services/test-helpers.ts
@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.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

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

Channel pr-2417
Deep Link dofek://preview/pr-2417
Commit 25c8a58

To test on device:

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

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

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 25c8a58f are ready:

This comment updates automatically on each PR push.

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/server/src/contracts/mobile-dashboard-contracts.ts (1)

495-507: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Include decisionContext.latestMeasurement.date in the fixture date-window check.

validateDatesInWindow checks the dates of hrvVariability, hrvBaseline, readinessScore, stress.daily, dailyMetrics, and weight against input's window. The new decisionContext.latestMeasurement.date field is the same kind of date-bearing field but is not included. A story or test fixture could set an out-of-window decisionContext.latestMeasurement.date without this schema catching it.

Add it to the validated list, handling the nullable decisionContext/latestMeasurement.

🧩 Proposed fix
     validateDatesInWindow(
       input,
       [
         ...data.hrvVariability.map((row) => row.date),
         ...data.hrvBaseline.map((row) => row.date),
         ...data.readinessScore.map((row) => row.date),
         ...data.stress.daily.map((row) => row.date),
         ...data.dailyMetrics.map((row) => row.date),
         ...data.weight.map((row) => row.date),
+        ...(data.decisionContext?.latestMeasurement
+          ? [data.decisionContext.latestMeasurement.date]
+          : []),
       ],
       context,
     );
🤖 Prompt for 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.

In `@packages/server/src/contracts/mobile-dashboard-contracts.ts` around lines 495
- 507, Update the date list passed to validateDatesInWindow in the surrounding
superRefine callback to include decisionContext.latestMeasurement.date when both
decisionContext and latestMeasurement are present, while safely omitting it when
either is null or absent. Preserve the existing validation for all other
date-bearing collections.
🤖 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/body-metrics-decision-context.md`:
- Around line 37-38: Update the threshold description in the decision-context
documentation to refer to fewer than eight residual observations, or valid
measurement/Trend Weight pairs, rather than actual readings; preserve the
existing insufficient-data behavior.

In `@docs/production-incident-baseline.md`:
- Line 162: Insert one blank line immediately before the incident heading
“2026-08-02: Mobile Preview OTA workflow exposed an unmasked Infisical
credential” in docs/production-incident-baseline.md, preserving the heading text
and GitHub-flavored Markdown structure.

---

Outside diff comments:
In `@packages/server/src/contracts/mobile-dashboard-contracts.ts`:
- Around line 495-507: Update the date list passed to validateDatesInWindow in
the surrounding superRefine callback to include
decisionContext.latestMeasurement.date when both decisionContext and
latestMeasurement are present, while safely omitting it when either is null or
absent. Preserve the existing validation for all other date-bearing collections.
🪄 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: bb13d318-1689-461d-9fc8-63fae30175fb

📥 Commits

Reviewing files that changed from the base of the PR and between fdab846 and 14c6eb2.

📒 Files selected for processing (21)
  • .github/actions/load-infisical-secrets/action.yml
  • docs/README.md
  • docs/body-metrics-decision-context.md
  • docs/production-incident-baseline.md
  • packages/format/package.json
  • packages/format/src/body-decision-context.test.ts
  • packages/format/src/body-decision-context.ts
  • packages/mobile/app/(tabs)/recovery.stories.tsx
  • packages/mobile/app/(tabs)/recovery.test.tsx
  • packages/mobile/app/(tabs)/recovery.tsx
  • packages/mobile/components/BodyDecisionContext.stories.tsx
  • packages/mobile/components/BodyDecisionContext.test.tsx
  • packages/mobile/components/BodyDecisionContext.tsx
  • packages/server/src/contracts/body-decision-context.ts
  • packages/server/src/contracts/mobile-dashboard-contracts.test.ts
  • packages/server/src/contracts/mobile-dashboard-contracts.ts
  • packages/server/src/repositories/body-analytics-repository.test.ts
  • packages/server/src/repositories/body-analytics-repository.ts
  • packages/server/src/repositories/body-clickhouse.test.ts
  • packages/server/src/repositories/body-clickhouse.ts
  • packages/server/src/repositories/body-repository.integration.test.ts

Comment thread docs/body-metrics-decision-context.md Outdated
Comment thread docs/production-incident-baseline.md
@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 commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Also fixed the outside-diff review finding: mobile recovery fixture validation now includes nullable decisionContext.latestMeasurement.date in its date-window checks, with regression coverage.

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

1 similar comment
@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 2b7ad5a into main Aug 3, 2026
108 checks passed
@Asherlc
Asherlc deleted the codex/issue-2145 branch August 3, 2026 08:58
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.

[BODY-04] Body metrics lack measurement method/source for trend decisions

2 participants