Skip to content

Add personalization model cards - #2303

Merged
Asherlc merged 11 commits into
mainfrom
Asherlc/issue-2180-algorithm-model-cards
Jul 30, 2026
Merged

Asherlc merged 11 commits into
mainfrom
Asherlc/issue-2180-algorithm-model-cards

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • record successful-fit timestamps independently for all five personalization models
  • build canonical server-owned model cards with source windows, minimum data, fit statistics, exclusions, uncertainty limits, and default status
  • render the same accessible evidence in web and mobile settings, with matching stories

Evidence boundaries

  • fittedAt is labeled as the latest refit attempt
  • legacy learned models report their successful-fit time as unavailable until refit
  • correlation and R² remain fit statistics; the UI does not relabel them as confidence
  • calibrated uncertainty and exact excluded-row counts are explicitly reported as unavailable

Validation

  • pnpm lint:sandbox
  • pnpm test:changed (910 tests)
  • pnpm tsc --noEmit
  • packages/server: pnpm tsc --noEmit
  • packages/web: pnpm typecheck
  • packages/mobile: pnpm typecheck

Fixes #2180

Summary by Sourcery

Introduce server-owned personalization model cards with per-model fit metadata and render them consistently across web and mobile settings.

New Features:

  • Expose personalization model cards from the server, describing status, data windows, sufficiency requirements, fit statistics, uncertainty, and excluded inputs for each model.
  • Add per-model successful-fit timestamps to personalization parameters to distinguish overall refit attempts from individual accepted fits.
  • Render accessible personalization model evidence cards in web and mobile Settings, including learned/default status, last successful fit messaging, and detailed data and fit evidence.

Bug Fixes:

  • Surface underlying server error messages in personalization panels instead of generic failure text.

Enhancements:

  • Document personalization evidence semantics, timestamp behavior, and model-card limitations in the personalization README.
  • Refine personalization fitters by exporting minimum data and quality thresholds as named constants for reuse in model-card generation.

Documentation:

  • Add a TDD plan describing the rollout of personalization model cards and evidence behavior across the stack.

Tests:

  • Extend server, web, and mobile tests to cover model-card contracts, legacy timestamp handling, and truthful rendering of unavailable fit-time and uncertainty evidence.
  • Add dedicated unit tests for the personalization model-card builder and updated params/refit logic, including schema versioning and successful-fit timestamp validation.

Summary by cubic

Adds server-built personalization model cards with truthful evidence and per-model successful-fit timestamps. Aligns Expo mobile dependencies for SDK 57 to restore Metro; meets #2180.

  • New Features

    • Server returns modelCards from personalization.status via dofek/personalization/model-card, including status, last successful fit, data window, data sufficiency, fit stats (Pearson/R²), uncertainty, and exclusions.
    • Params schema v2 adds optional per-model successfulFitAt; refitAllParams stamps only newly accepted fits; legacy params still validate.
    • Web and mobile render the same evidence; PersonalizationModelCard exported from dofek-server/types and dofek/personalization/model-card.
  • Bug Fixes

    • Always return five model cards with safe defaults; legacy learned models show “Successful fit time unavailable until this model is refit.”
    • Model-card generation reuses exported fitter thresholds and guards missing data to prevent drift.
    • Clearer server error when analytics are not configured (with logging); clients capture exceptions in personalization panels via telemetry.
    • Tests cover model-card contracts and ensure per-model successful-fit timestamps survive parsing and refit cycles.

Written for commit 8e54252. Summary will update on new commits.

Review in cubic

Copilot AI review requested due to automatic review settings July 29, 2026 19:23
@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.

@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

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.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Asherlc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cf6b6557-2aae-4d21-978d-953f2c3f34b9

📥 Commits

Reviewing files that changed from the base of the PR and between e3d0759 and 8e54252.

📒 Files selected for processing (27)
  • docs/production-incident-baseline.md
  • docs/superpowers/plans/2026-07-29-personalization-model-cards.md
  • package.json
  • packages/mobile/app/settings.stories.tsx
  • packages/mobile/components/PersonalizationPanel.stories.tsx
  • packages/mobile/components/PersonalizationPanel.test.tsx
  • packages/mobile/components/PersonalizationPanel.tsx
  • packages/server/src/repositories/personalization-repository.test.ts
  • packages/server/src/repositories/personalization-repository.ts
  • packages/server/src/routers/personalization.test.ts
  • packages/server/src/routers/personalization.ts
  • packages/server/src/types.ts
  • packages/web/src/components/PersonalizationPanel.stories.tsx
  • packages/web/src/components/PersonalizationPanel.test.tsx
  • packages/web/src/components/PersonalizationPanel.tsx
  • src/personalization/README.md
  • src/personalization/fit-ewma.ts
  • src/personalization/fit-readiness-weights.ts
  • src/personalization/fit-sleep-target.ts
  • src/personalization/fit-stress-thresholds.ts
  • src/personalization/fit-trimp.ts
  • src/personalization/model-card.test.ts
  • src/personalization/model-card.ts
  • src/personalization/params.test.ts
  • src/personalization/params.ts
  • src/personalization/refit.test.ts
  • src/personalization/refit.ts

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.

@sourcery-ai

sourcery-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds server-owned personalization model cards with truthful evidence metadata, wires them through the personalization status API, and updates web/mobile panels, tests, stories, and refit logic to track per-model successful-fit timestamps under a new schema version.

File-Level Changes

Change Details Files
Introduce canonical server-built personalization model cards and expose them via the personalization status API.
  • Define PersonalizationModelCard type and builder logic that derives evidence fields (status, data window, data sufficiency, fit statistics, uncertainty, exclusions) from PersonalizedParams and fitter contracts.
  • Export model-card module via package.json and re-export PersonalizationModelCard from server types for client consumption.
  • Update PersonalizationRepository.getStatus to include modelCards built from stored params, with tests ensuring default and personalized states are correctly reported.
  • Add unit tests for model-card construction covering learned, default, and legacy timestamp behavior and exact wording of evidence fields.
src/personalization/model-card.ts
src/personalization/model-card.test.ts
package.json
packages/server/src/types.ts
packages/server/src/repositories/personalization-repository.ts
packages/server/src/repositories/personalization-repository.test.ts
Extend personalization parameter schema and refit engine to track per-model successful-fit timestamps and bump schema version.
  • Add PersonalizationModelKey, successfulFitAt schema, and export constants for minimum data/quality thresholds used by fitters.
  • Update personalizedParamsSchema to accept optional successfulFitAt for backward compatibility and add tests for legacy params, valid/invalid timestamps, and version semantics.
  • Modify refitAllParams to set version 2, record a common fittedAt attempt timestamp, and populate successfulFitAt per model only when a new fit is accepted, preserving existing timestamps when fits are rejected or insufficient.
  • Adjust refit tests to expect version 2, validate successfulFitAt behavior in no-fit, preserved-fit, and newly-accepted-fit scenarios, and add helper data generator for EWMA rows.
  • Update individual fitter modules to export their minimum data/quality constants for use in model-card evidence, without changing fitting behavior.
  • Document fit timestamps and evidence limits in personalization README.
src/personalization/params.ts
src/personalization/params.test.ts
src/personalization/refit.ts
src/personalization/refit.test.ts
src/personalization/fit-ewma.ts
src/personalization/fit-readiness-weights.ts
src/personalization/fit-trimp.ts
src/personalization/fit-sleep-target.ts
src/personalization/fit-stress-thresholds.ts
src/personalization/README.md
Update web and mobile PersonalizationPanel components to render server-provided model cards, improve accessibility, and surface server error messages directly.
  • Replace local PARAM_LABELS and per-param quality derivation with PersonalizationModelCard-driven rendering, including status, title, description, last successful fit or summary, data window, data sufficiency, fit evidence, uncertainty, and excluded data.
  • Add getModelCard helper to safely bind each parameter display to the corresponding model card, throwing on missing cards.
  • Enhance accessibility by labeling headers/articles as model evidence groups and using semantic elements (header roles, ,
    /
    /
    for web).
  • Change top-level fittedAt copy to “Last refit attempt” and error rendering to show status.error.message verbatim.
  • Add EvidenceRow helpers and styles/markup for evidence sections and exclusion lists.
packages/mobile/components/PersonalizationPanel.tsx
packages/web/src/components/PersonalizationPanel.tsx
Extend web and mobile tests and stories to cover model card evidence rendering, error messaging, and default/personalized scenarios.
  • Augment PersonalizationPanel tests on mobile and web to seed mockData.modelCards consistent with server contract, verify rendering of data window, sufficiency, fit evidence, uncertainty text, and excluded data, and assert that UIs do not relabel correlation/R² as confidence.
  • Add tests confirming truthful handling of unavailable successful-fit times for legacy models and that server error messages are surfaced directly.
  • Update Storybook stories for PersonalizationPanel on web and mobile to include modelCards in personalization status and to model default vs personalized evidence states.
  • Adjust mobile settings stories to seed default modelCards and a non-personalized personalization.status, aligning the Settings screen with the new evidence contract.
packages/mobile/components/PersonalizationPanel.test.tsx
packages/web/src/components/PersonalizationPanel.test.tsx
packages/mobile/components/PersonalizationPanel.stories.tsx
packages/web/src/components/PersonalizationPanel.stories.tsx
packages/mobile/app/settings.stories.tsx
Add a developer-facing TDD plan documenting the personalization model cards work. docs/superpowers/plans/2026-07-29-personalization-model-cards.md

Assessment against linked issues

Issue Objective Addressed Explanation
#2180 Add server-owned personalization model cards that expose, for each model, last successful fit time, data window, data sufficiency, fit statistics and uncertainty, excluded data, and default-versus-personalized status in the canonical personalization schema and API.
#2180 Render the personalization model cards in both web and mobile Settings panels, showing the same evidence fields (last fit, data window, sufficiency, fit statistics, uncertainty, excluded data, and default vs learned state) in an accessible way without relabeling fit statistics as confidence or inventing unavailable data.
#2180 Document the behavior and limits of personalization evidence (fit timestamps, data requirements, uncertainty, and model cards) in the personalization documentation.

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

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

Channel pr-2303
Deep Link dofek://preview/pr-2303
Commit b1ae577

To test on device:

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

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add server-owned personalization model cards and render in web/mobile Settings

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Track per-model successful-fit timestamps separately from overall refit attempts.
• Generate canonical, server-built personalization model cards with evidence and limits.
• Render identical evidence cards in web and mobile Settings, backed by tests/stories.
Diagram

graph TD
  web["Web PersonalizationPanel"] --> api["TRPC personalization.status"] --> repo["PersonalizationRepository"] --> cards["Model card builder"]
  mobile["Mobile PersonalizationPanel"] --> api
  repo --> db[(user_settings_JSON)]
  refit["refitAllParams"] --> db
  cards --> fitters["Fitters + thresholds"]
  refit --> fitters
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Return structured evidence fields (numbers/enums) instead of preformatted strings
  • ➕ Enables localization and consistent wording across clients
  • ➕ Allows richer UI without changing server copy
  • ➕ Reduces brittleness of string-matching tests
  • ➖ Requires more client formatting logic and shared presentation rules
  • ➖ Harder to guarantee identical phrasing across platforms without shared UI layer
2. Client-built model cards from params + shared fitter metadata module
  • ➕ Avoids duplicating evidence text in server responses
  • ➕ Lets clients evolve UI independently
  • ➖ Reintroduces drift between web/mobile and server semantics
  • ➖ Clients may be tempted to infer "confidence"/uncertainty despite evidence limits

Recommendation: Current approach (server-owned, canonical model cards) is the right default because it prevents web/mobile drift and enforces truthful evidence boundaries in one place. If localization or UI flexibility becomes a priority, consider evolving the contract to carry structured evidence fields while keeping the server as the source of truth for limits/availability.

Files changed (25) +1095 / -180

Enhancement (7) +367 / -150
PersonalizationPanel.tsxRender model evidence cards from server-provided modelCards (mobile) +69/-74

Render model evidence cards from server-provided modelCards (mobile)

• Replaces hardcoded parameter labels/quality strings with server-owned model cards, including accessibility labels and evidence rows (fit time, window, sufficiency, fit stats, uncertainty, exclusions). Updates error handling to display server error messages and renames fittedAt label to "Last refit attempt".

packages/mobile/components/PersonalizationPanel.tsx

personalization-repository.tsAttach server-built modelCards to personalization status response +6/-0

Attach server-built modelCards to personalization status response

• Extends PersonalizationStatus to include modelCards and populates it via buildPersonalizationModelCards(stored). Keeps existing defaults/effective/parameters behavior intact.

packages/server/src/repositories/personalization-repository.ts

types.tsRe-export PersonalizationModelCard type for clients +2/-0

Re-export PersonalizationModelCard type for clients

• Exposes the PersonalizationModelCard type via dofek-server/types so web/mobile can type the modelCards contract.

packages/server/src/types.ts

PersonalizationPanel.tsxRender model evidence cards from server-provided modelCards (web) +55/-74

Render model evidence cards from server-provided modelCards (web)

• Replaces client-derived quality strings and hardcoded metadata with server-provided model cards rendered as accessible <article> sections. Updates error handling to show server error messages and renames fittedAt label to "Last refit attempt".

packages/web/src/components/PersonalizationPanel.tsx

model-card.tsBuild canonical personalization model cards on the server +189/-0

Build canonical personalization model cards on the server

• Adds a model-card builder that emits five cards with learned/default status, per-model successful-fit time (or truthful unavailability), data windows, sufficiency requirements, fit statistics (Pearson/R² where available), explicit uncertainty unavailability, and exclusion rules.

src/personalization/model-card.ts

params.tsAdd model-key enum and optional successfulFitAt timestamp map to schema +21/-0

Add model-key enum and optional successfulFitAt timestamp map to schema

• Defines a canonical list of personalization model keys and introduces an optional successfulFitAt object keyed by model. Keeps schema backward compatible by making successfulFitAt optional.

src/personalization/params.ts

refit.tsRecord per-model successfulFitAt when a fitter returns a new accepted result +25/-2

Record per-model successfulFitAt when a fitter returns a new accepted result

• Moves personalization schema to version 2 and records a single refit attempt time (fittedAt) plus per-model successfulFitAt only when that model fit is newly accepted. Preserves existing successfulFitAt values for models whose prior fits are retained, and leaves legacy learned models without timestamps until refit.

src/personalization/refit.ts

Refactor (5) +17 / -16
fit-ewma.tsExport EWMA sufficiency/quality thresholds as constants +5/-4

Export EWMA sufficiency/quality thresholds as constants

• Promotes minimum sample and correlation thresholds to named exports for reuse by model-card generation, and updates fitter logic to reference them.

src/personalization/fit-ewma.ts

fit-readiness-weights.tsExport readiness sufficiency/quality thresholds as constants +4/-4

Export readiness sufficiency/quality thresholds as constants

• Exports minimum days and minimum correlation thresholds and reuses them in fitter gating logic to keep model-card evidence consistent.

src/personalization/fit-readiness-weights.ts

fit-sleep-target.tsExport minimum qualifying nights constant +2/-2

Export minimum qualifying nights constant

• Exports the minimum qualifying nights threshold and updates fitter logic to use the named constant for shared evidence.

src/personalization/fit-sleep-target.ts

fit-stress-thresholds.tsExport minimum stress days constant +2/-2

Export minimum stress days constant

• Exports the minimum days threshold and updates the fitter to reference it for shared evidence generation.

src/personalization/fit-stress-thresholds.ts

fit-trimp.tsExport TRIMP sufficiency/quality thresholds as constants +4/-4

Export TRIMP sufficiency/quality thresholds as constants

• Exports minimum qualifying activities and minimum R² thresholds and reuses them in the fitter gate to align with model-card evidence wording.

src/personalization/fit-trimp.ts

Tests (7) +456 / -11
PersonalizationPanel.test.tsxTest mobile rendering of server-built model evidence and error messages +115/-2

Test mobile rendering of server-built model evidence and error messages

• Extends mocked personalization.status data with modelCards and verifies evidence fields render without "confidence" derivation. Adds a test to surface server error messages verbatim and validates unavailable fit-time messaging.

packages/mobile/components/PersonalizationPanel.test.tsx

personalization-repository.test.tsAssert modelCards are returned in personalization status +9/-1

Assert modelCards are returned in personalization status

• Adds coverage that getStatus returns five model cards and that default cards have status=default. Verifies learned cards reflect legacy timestamp unavailability when applicable.

packages/server/src/repositories/personalization-repository.test.ts

personalization.test.tsValidate router status includes modelCards contract +8/-0

Validate router status includes modelCards contract

• Updates router tests to assert modelCards length/status for default responses and verifies personalized responses include legacy-fit timestamp messaging when successfulFitAt is absent.

packages/server/src/routers/personalization.test.ts

PersonalizationPanel.test.tsxTest web rendering of server-built model evidence and error messages +114/-1

Test web rendering of server-built model evidence and error messages

• Adds modelCards to mock status data and verifies evidence sections render (window/sufficiency/fit/uncertainty/exclusions) without confidence wording. Adds tests for surfacing server error messages and truthful unavailable fit-time text.

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

model-card.test.tsAdd unit tests for server model-card contract semantics +115/-0

Add unit tests for server model-card contract semantics

• Introduces tests ensuring stable card ordering, correct evidence wording, legacy timestamp behavior, and explicit non-claims about confidence/uncertainty. Validates detailed source windows and exclusion lists for TRIMP.

src/personalization/model-card.test.ts

params.test.tsTest backward-compatible parsing of successfulFitAt per-model timestamps +37/-0

Test backward-compatible parsing of successfulFitAt per-model timestamps

• Adds schema tests ensuring legacy params remain valid without successfulFitAt, valid timestamp maps parse correctly, and invalid timestamps are rejected.

src/personalization/params.test.ts

refit.test.tsUpdate refit tests for schema v2 and per-model successful-fit timestamps +58/-7

Update refit tests for schema v2 and per-model successful-fit timestamps

• Bumps expected schema version to 2, asserts successfulFitAt is present and preserved across insufficient-data refits, and verifies newly accepted fits record the attempt timestamp for that model only.

src/personalization/refit.test.ts

Documentation (2) +91 / -1
2026-07-29-personalization-model-cards.mdAdd TDD plan for personalization model cards rollout +86/-0

Add TDD plan for personalization model cards rollout

• Introduces a task-based plan covering schema changes, server contract generation, client parity rendering, and validation steps. Documents evidence boundaries to avoid overstating confidence/uncertainty.

docs/superpowers/plans/2026-07-29-personalization-model-cards.md

README.mdDocument model cards, timestamps, and evidence limits +5/-1

Document model cards, timestamps, and evidence limits

• Adds documentation for server-owned model cards, the difference between fittedAt and per-model successfulFitAt, and explicit evidence limitations (no calibrated uncertainty, no exclusion counts).

src/personalization/README.md

Other (4) +164 / -2
package.jsonExport personalization/model-card entrypoint +1/-0

Export personalization/model-card entrypoint

• Adds a package subpath export for the new server-consumed model-card builder module.

package.json

settings.stories.tsxSeed Settings story with default personalization modelCards +35/-2

Seed Settings story with default personalization modelCards

• Updates the seeded personalization.status query data to include a modelCards array and align the story with the new API shape. Adjusts default scenario to isPersonalized=false and fittedAt=null.

packages/mobile/app/settings.stories.tsx

PersonalizationPanel.stories.tsxAdd modelCards fixtures for mobile PersonalizationPanel stories +64/-0

Add modelCards fixtures for mobile PersonalizationPanel stories

• Extends story fixtures to include server-style modelCards evidence for both default and personalized scenarios. Ensures storybook reflects the full server contract.

packages/mobile/components/PersonalizationPanel.stories.tsx

PersonalizationPanel.stories.tsxAdd modelCards fixtures for web PersonalizationPanel stories +64/-0

Add modelCards fixtures for web PersonalizationPanel stories

• Updates story fixtures to include modelCards evidence for default and personalized scenarios, matching the server contract shape and wording.

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

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for b1ae5775 are ready:

This comment updates automatically on each PR push.

@qodo-code-review

qodo-code-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 212 rules

Grey Divider


Remediation recommended

1. Throw on missing cards ✓ Resolved 🐞 Bug ☼ Reliability
Description
Both web and mobile call getModelCard(...) during render and throw if a card is missing, which
will crash the whole panel (and potentially the Settings screen) rather than degrading gracefully.
This makes the clients brittle to any unexpected/partial status payload.
Code

packages/web/src/components/PersonalizationPanel.tsx[R196-205]

+function getModelCard(
+  modelCards: PersonalizationModelCard[],
+  key: PersonalizationModelCard["key"],
+): PersonalizationModelCard {
+  const modelCard = modelCards.find((candidate) => candidate.key === key);
+  if (!modelCard) {
+    throw new Error(`Missing personalization model card: ${key}`);
+  }
+  return modelCard;
+}
Relevance

●●● Strong

They’ve accepted avoiding render-path crashes on malformed payloads; throwing on missing cards will
likely be changed to graceful handling.

PR-#2208

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
getModelCard() explicitly throws when a requested key isn’t found, and both clients invoke it for
each parameter card during render, making any missing key a render-time crash.

packages/web/src/components/PersonalizationPanel.tsx[59-61]
packages/web/src/components/PersonalizationPanel.tsx[196-205]
packages/mobile/components/PersonalizationPanel.tsx[74-103]
packages/mobile/components/PersonalizationPanel.tsx[205-214]

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

### Issue description
`getModelCard()` throws during render when a model card is absent. Because it’s invoked unconditionally for all five cards, a malformed/incomplete `modelCards` array will raise a render-time exception and break the panel.

### Issue Context
- Web and mobile both implement `getModelCard()` as a throwing lookup.
- Both panels call it directly when rendering each card.

### Fix Focus Areas
- Change `getModelCard` to return `null`/`undefined` and render an inline error state for that card (or fall back to older label/description and hide evidence rows).
- Optionally assert/log missing keys (e.g., `console.error`/Sentry) without throwing.

- packages/web/src/components/PersonalizationPanel.tsx[59-61]
- packages/web/src/components/PersonalizationPanel.tsx[196-205]
- packages/mobile/components/PersonalizationPanel.tsx[74-103]
- packages/mobile/components/PersonalizationPanel.tsx[205-214]

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


2. Model cards hardcode days counts 📘 Rule violation ⚙ Maintainability
Description
src/personalization/model-card.ts builds user-facing strings by manually concatenating numeric
values with units like days, nights, and activities, instead of using the shared
unit-formatting mechanisms. This can lead to inconsistent formatting/localization across clients and
violates the unit-formatting compliance requirement.
Code

src/personalization/model-card.ts[R74-83]

+    description: "How many days of training history are used to compute fitness and fatigue",
+    status: personalized ? "personalized" : "default",
+    ...fitTime(params, "exponentialMovingAverage", personalized),
+    dataWindow: "Past 365 days",
+    dataSufficiency: fitted
+      ? `${fitted.sampleCount} qualifying days used; minimum ${MINIMUM_EXPONENTIAL_MOVING_AVERAGE_DAYS} days`
+      : `No accepted fit; requires at least ${MINIMUM_EXPONENTIAL_MOVING_AVERAGE_DAYS} qualifying days and absolute Pearson correlation of at least ${MINIMUM_EXPONENTIAL_MOVING_AVERAGE_ABSOLUTE_CORRELATION.toFixed(2)}`,
+    fitEvidence: fitted
+      ? `Pearson correlation: ${fitted.correlation.toFixed(3)}`
+      : "No accepted fit statistic is available.",
Relevance

●●● Strong

Repo enforces shared unit/measurement formatting in user-facing strings; hardcoded
“days/nights/activities” likely to be refactored.

PR-#1963
PR-#1215
PR-#1123

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 722094 requires shared unit-formatting mechanisms for user-facing
measurements/counts. The model-card builder constructs strings like Past 365 days and
${fitted.sampleCount} qualifying days used; minimum ${...} days via template literals, which is
manual unit formatting.

Rule 722094: Use shared unit formatting hooks/contexts for all user-facing measurements
src/personalization/model-card.ts[74-83]

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

## Issue description
`buildPersonalizationModelCards()` returns user-facing text that manually formats measurements/counts (e.g., `Past 365 days`, `${sampleCount} qualifying days used; minimum ${MINIMUM_*} days`). The compliance checklist requires user-facing measurements to be formatted via shared unit-formatting hooks/contexts (or a shared formatting utility), not ad-hoc string concatenation.

## Issue Context
These strings are rendered in both web and mobile settings panels, so manual formatting here becomes the canonical display and bypasses the project’s shared unit formatting approach.

## Fix Focus Areas
- src/personalization/model-card.ts[66-189]

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


3. Internal errors leaked ✓ Resolved 🐞 Bug ⛨ Security
Description
PersonalizationPanel now renders status.error.message verbatim, which can expose internal
operational/configuration details to end users (e.g., the server’s ClickHouse precondition error
mentions CLICKHOUSE_URL). This is a behavior change from the previous generic client message and
increases information disclosure risk when backend messages aren’t explicitly end-user-safe.
Code

packages/web/src/components/PersonalizationPanel.tsx[R33-35]

  if (status.error) {
-    return <p className="text-sm text-red-400">Failed to load personalization status</p>;
+    return <p className="text-sm text-red-400">{status.error.message}</p>;
  }
Relevance

●● Moderate

Mixed signals: team recently surfaced server error messages, but also avoids leaking internal
reasons; unclear if they’ll revert.

PR-#2207
PR-#1960

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both clients now directly render status.error.message, and the server’s requireSensorStore()
throws a message containing internal config instructions (Set CLICKHOUSE_URL and retry.), which
will now be displayed verbatim.

packages/web/src/components/PersonalizationPanel.tsx[33-35]
packages/mobile/components/PersonalizationPanel.tsx[33-35]
packages/server/src/routers/personalization.ts[7-16]

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

### Issue description
`PersonalizationPanel` (web + mobile) now displays `status.error.message` directly. Some server error messages contain internal configuration/operational guidance (e.g., mentioning `CLICKHOUSE_URL`), which should not be shown to end users.

### Issue Context
- Web renders raw message on `status.error`.
- Mobile renders raw message on `status.error`.
- Server `personalization` router throws a `PRECONDITION_FAILED` with a message instructing setting `CLICKHOUSE_URL`.

### Fix Focus Areas
- Prefer server-side user-safe messages for this router’s errors (log detailed config guidance server-side).
- Optionally add client-side fallback mapping for “technical” errors to a friendly message.

- packages/web/src/components/PersonalizationPanel.tsx[33-35]
- packages/mobile/components/PersonalizationPanel.tsx[33-35]
- packages/server/src/routers/personalization.ts[7-16]

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



Informational

4. Unvalidated status response 🐞 Bug ⚙ Maintainability
Description
The server expands personalization.status with a modelCards contract, but the router still
provides no runtime .output(...) validation for the response shape. This reduces defense-in-depth
against malformed runtime values and makes contract regressions harder to catch at the API boundary.
Code

packages/server/src/repositories/personalization-repository.ts[107]

+      modelCards: buildPersonalizationModelCards(stored),
Relevance

●●● Strong

Team often adds tRPC .output Zod schemas to prevent contract drift; new field should be validated.

PR-#2209
PR-#2015
PR-#2045

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The repository now returns modelCards as part of the status object, while
personalizationRouter.status returns repo.getStatus() without any output schema; past accepted
guidance recommends adding tRPC output schemas to runtime-validate contracts.

packages/server/src/repositories/personalization-repository.ts[20-27]
packages/server/src/repositories/personalization-repository.ts[84-108]
packages/server/src/routers/personalization.ts[20-45]
PR-#2209

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

### Issue description
`personalization.status` now includes `modelCards`, but the tRPC procedure does not validate outputs at runtime. Adding output schemas improves contract safety (especially for runtime-generated text fields and future refactors).

### Issue Context
Other parts of the codebase recently moved toward explicit tRPC input/output Zod validation as a standard pattern.

### Fix Focus Areas
- Define Zod schemas for `PersonalizationModelCard` and `PersonalizationStatus`.
- Attach `.output(...)` to `personalization.status` (and consider `refit`/`reset` as well).

- packages/server/src/repositories/personalization-repository.ts[20-27]
- packages/server/src/repositories/personalization-repository.ts[84-108]
- packages/server/src/routers/personalization.ts[20-45]

ⓘ 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 src/personalization/model-card.ts
Comment thread packages/web/src/components/PersonalizationPanel.tsx
Comment thread packages/web/src/components/PersonalizationPanel.tsx Outdated
Comment thread packages/server/src/repositories/personalization-repository.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.

@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 commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

CI root-cause fix pushed.

  • Failed step: Build Mobile / Metro Bundle → Verify dependencies match Expo SDK.
  • First fatal output: Found outdated dependencies.
  • Cause: Expo SDK 57 live compatibility metadata advanced after the previous successful run, while this branch and origin/main still had identical older exact pins.
  • Direct fix: updated all 12 packages to Expo’s compatible exact versions and regenerated the lockfile; no validation exclusion, retry, fallback, or release-age exception was retained.
  • Local proof: Expo dependency check, frozen install, exact-version policy, mobile typecheck, focused mobile tests, lint:sandbox, and a clean 2,919-module iOS export pass.
  • Commit: 72644cb41

Final resolution update (2026-07-29): This earlier 12-package refresh was superseded by the complete canonical SDK graph on main (including Expo 57.0.9 and React Native 0.86.2). PR #2303 merged that manifest and lockfile at head 09948b92314f6adc68be7e07c4944a138c5ae893; frozen install and Expo dependency validation both pass with no policy exception or workaround. Fresh exact-head CI is running.

@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 the canonical Zod boundary in the dynamic refit test so per-test mutation coverage verifies every model timestamp survives parsing.
@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 commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

CI mutation root-cause fix pushed.

  • Failed job: Test / Stryker (2), job 90712301245.
  • Exact command: pnpm exec stryker run stryker.ci.config.json --mutate "src/personalization/params.ts:58-77,src/personalization/params.ts:81-81".
  • First fatal mutation result: [Survived] ObjectLiteral at src/personalization/params.ts:68:40; replacing the five-field timestamp schema with z.object({}) survived.
  • Cause: per-test mutation coverage selected the dynamic all-fitters refit test, which asserted only the pre-validation result and did not prove the timestamp map survived Zod parsing.
  • Direct fix: the selected regression now parses the refit result through personalizedParamsSchema and verifies all five timestamps remain intact.
  • Proof: the focused refit test passes; the exact prior Stryker shard now kills 1/1 mutants (100%); 920 changed tests, lint:sandbox, and root/server/web/mobile typechecks pass.
  • Commit: fdf3fafa6

@Asherlc
Asherlc enabled auto-merge (squash) July 30, 2026 13:31
@Asherlc
Asherlc merged commit 1fed131 into main Jul 30, 2026
3 of 4 checks passed
@Asherlc
Asherlc deleted the Asherlc/issue-2180-algorithm-model-cards branch July 30, 2026 13:41
@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.

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.

[SET-07] Algorithm transparency lacks fit, sufficiency, confidence, default status

2 participants