Skip to content

fix(breathwork): show safety before sessions - #2243

Merged
Asherlc merged 4 commits into
mainfrom
codex/issue-2066
Jul 28, 2026
Merged

Asherlc merged 4 commits into
mainfrom
codex/issue-2066

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • replace categorical breathwork health claims with technique-specific, possible-benefit wording only where supported
  • add server-owned positioning, stop, emergency, and Wim Hof loss-of-consciousness/water/driving guidance
  • render material guidance before Start on web and a new mobile Breathwork route linked from Recovery

Evidence

Validation

  • mise exec -- pnpm lint
  • mise exec -- pnpm typecheck
  • mise exec -- pnpm --dir packages/server exec tsc --noEmit
  • mise exec -- pnpm --dir packages/web exec tsc --noEmit
  • mise exec -- pnpm --dir packages/mobile exec tsc --noEmit
  • mise exec -- pnpm test -- --run (906 files passed; 14,289 tests passed; 21 skipped)
  • targeted breathwork/server/web/mobile/Recovery suite (44 tests passed)

Fixes #2066

Summary by Sourcery

Add calibrated, evidence-backed safety guidance and possible-benefit messaging to all breathwork techniques, and ensure this guidance is shown before starting sessions on web and mobile, including a new mobile breathwork screen accessible from Recovery.

New Features:

  • Introduce technique-level possible benefit messaging for breathwork exercises where evidence supports it.
  • Expose structured safety guidance for each breathwork technique from the scoring/server layer to clients.
  • Add a dedicated breathwork screen to the mobile app with guided sessions and pre-session safety messaging, linked from the Recovery tab.

Enhancements:

  • Update breathwork technique descriptions to remove categorical health claims and align wording with cited evidence.
  • Adjust web breathwork page copy and layout to highlight safety guidance before starting a session.

Tests:

  • Extend server, web, scoring, and mobile tests to cover safety guidance presence, Wim Hof-specific warnings, and possible-benefit constraints.
  • Add mobile storybook coverage for the new breathwork screen backed by shared technique data.

Summary by cubic

Show safety guidance before breathwork sessions on web and mobile, rename the fixed cadence to “Power Breathing” with its real timing, and use evidence-based “possible” benefits where supported (Fixes #2066). Harden the mobile guided timer to prevent duplicate starts and only schedule at phase boundaries.

  • New Features

    • @dofek/scoring: add safety (position, warnings, stop criteria, emergency) and technique-level possibleBenefit; rename Wim Hof to “Power Breathing” with 2s in/2s out ×30 rounds and material risk warnings.
    • packages/web: show safety and possible benefits before Start; update subtitle to reflect safety guidance.
    • packages/mobile: new /breathwork screen with technique selector, pre-session safety, guided timer, and session logging; add Recovery → Breathwork link.
  • Bug Fixes

    • packages/mobile: guard Start to avoid duplicate timers/logs and schedule transitions only at phase boundaries; Storybook tRPC operations are explicit and fail fast.

Written for commit 496860d. Summary will update on new commits.

Review in cubic

Replace categorical health claims with evidence-bound optional language. Put loss-of-consciousness, positioning, stop, and emergency guidance in server-owned technique data rendered by web and mobile.

Refs #2066
Copilot AI review requested due to automatic review settings July 28, 2026 06:06
@cursor

cursor Bot commented Jul 28, 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 28, 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: 20 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: ba210bc7-3712-4acc-bbad-c48fa3ef1a17

📥 Commits

Reviewing files that changed from the base of the PR and between 71e880b and 496860d.

📒 Files selected for processing (11)
  • packages/mobile/app/(tabs)/recovery.test.tsx
  • packages/mobile/app/(tabs)/recovery.tsx
  • packages/mobile/app/_layout.tsx
  • packages/mobile/app/breathwork.stories.tsx
  • packages/mobile/app/breathwork.test.tsx
  • packages/mobile/app/breathwork.tsx
  • packages/scoring/src/breathwork.test.ts
  • packages/scoring/src/breathwork.ts
  • packages/server/src/routers/breathwork.test.ts
  • packages/web/src/routes/breathwork.test.tsx
  • packages/web/src/routes/breathwork.tsx

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 28, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds structured safety guidance and evidence-calibrated benefit messaging to breathwork techniques, ensures this guidance is rendered before starting sessions on web and mobile, introduces a dedicated mobile Breathwork screen reachable from Recovery, and tightens server/shared typing and tests around safety and benefits.

Sequence diagram for mobile Recovery to Breathwork with pre-session safety

sequenceDiagram
  actor User
  participant RecoveryScreen
  participant Router
  participant BreathworkScreen
  participant trpc_breathwork_techniques as trpc.breathwork.techniques.useQuery
  participant trpc_breathwork_logSession as trpc.breathwork.logSession.useMutation

  User->>RecoveryScreen: Tap Breathwork nav link
  RecoveryScreen->>Router: push(/breathwork)
  Router-->>BreathworkScreen: Show BreathworkScreen
  BreathworkScreen->>trpc_breathwork_techniques: useQuery()
  trpc_breathwork_techniques-->>BreathworkScreen: techniques with safety and possibleBenefit
  BreathworkScreen-->>User: Display safety and optional possibleBenefit
  User->>BreathworkScreen: Tap Start Session
  BreathworkScreen-->>BreathworkScreen: Run timed breathing phases
  BreathworkScreen->>trpc_breathwork_logSession: mutate(completedSession)
  trpc_breathwork_logSession-->>BreathworkScreen: onSuccess() clears pendingSession
Loading

File-Level Changes

Change Details Files
Introduce structured safety metadata and calibrated possible-benefit messaging for breathwork techniques in shared scoring layer and enforce via tests.
  • Extend BreathworkTechnique with optional possibleBenefit and required safety field wired to a new BreathworkSafetyGuidance interface.
  • Define STANDARD_SAFETY and WIM_HOF_SAFETY constants with NHS- and Wim-Hof-aligned wording and apply to all techniques.
  • Update technique descriptions to remove categorical health claims and set possibleBenefit only for techniques supported by the cited trial, with tests ensuring safety and benefits configuration is correct.
packages/scoring/src/breathwork.ts
packages/scoring/src/breathwork.test.ts
Render technique-specific safety guidance and calibrated benefit copy before starting sessions on the web breathwork page.
  • Update BreathworkPage subtitle to emphasize pre-session safety guidance.
  • Change pre-session UI to show technique name, description, optional possibleBenefit, and a safety panel with warnings, position, stop criteria, and emergency instructions above the Start Session button.
  • Extend existing web tests to include safety data in fixtures and add new cases asserting Wim Hof warnings and possibleBenefit rendering.
packages/web/src/routes/breathwork.tsx
packages/web/src/routes/breathwork.test.tsx
Add a new mobile Breathwork screen that surfaces server-owned techniques, shows safety guidance before Start, guides timed rounds, and logs completed sessions with robust error handling.
  • Implement BreathworkScreen with TRPC-backed loading of techniques, selection UI, safety guidance card, and start/stop session controls that drive a timer through inhale/hold/exhale phases and log sessions via logSession mutation.
  • Handle loading/empty/error states via QueryStatePanel and allow retrying failed session saves while preventing starting when a save is pending.
  • Add unit tests verifying safety/benefit rendering, Wim Hof warnings, session timing/logging behavior, and error-path UI.
packages/mobile/app/breathwork.tsx
packages/mobile/app/breathwork.test.tsx
Wire the new mobile Breathwork experience into navigation and Storybook, and expose breathwork from the Recovery tools.
  • Add a Breathwork navigation row to the RecoveryScreen that routes to the new /breathwork screen and test that it calls router.push correctly.
  • Register the breathwork screen in the root mobile Stack navigator.
  • Create a Storybook story that renders BreathworkScreen backed by a mocked TRPC link returning TECHNIQUES for local visual QA.
packages/mobile/app/(tabs)/recovery.tsx
packages/mobile/app/(tabs)/recovery.test.tsx
packages/mobile/app/_layout.tsx
packages/mobile/app/breathwork.stories.tsx
Tighten server router tests to assert safety guidance is present and Wim Hof warnings are propagated from the backend.
  • Extend breathworkRouter technique tests to require safety.position, stopCriteria, and emergency for every technique returned.
  • Add an assertion that the Wim Hof technique includes the loss-of-consciousness warning in its safety.warnings array.
packages/server/src/routers/breathwork.test.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#2066 Calibrate breathwork benefit claims to evidence, framing them as possible (non-guaranteed) benefits and removing overstated or categorical health claims. ✅
#2066 Provide practice-specific safety guidance for all techniques, including seated/lying positioning for breath-hold patterns, explicit stop criteria and emergency instructions, and Wim Hof-specific warnings about possible loss of consciousness and not practicing near water or while driving. ✅
#2066 Ensure users see the material safety information before starting a breathwork session on all supported surfaces (web and mobile). ✅

Tips and commands

Interacting with Sourcery

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

Customizing Your Experience

Access your dashboard to:

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

Getting Help

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Breathwork: show technique-specific safety guidance before sessions (web + mobile)

🐞 Bug fix ✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Move breathwork benefits to evidence-bound, technique-specific “possible benefit” copy.
• Add server-owned pre-session safety guidance (positioning, stop criteria, emergencies, Wim Hof
 warnings).
• Expose Breathwork on mobile via a new route linked from Recovery.
Diagram

graph TD
  U["User"] --> MR["Mobile Recovery"] --> MB["Mobile Breathwork"]
  U --> WB["Web Breathwork"]
  MB --> API["tRPC Breathwork Router"] --> SC["Scoring breathwork data"]
  WB --> API
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hard-gate start with an explicit safety acknowledgment
  • ➕ Stronger proof the guidance was seen/read before starting
  • ➕ Reduces risk of users skipping past the safety section
  • ➖ Adds friction to every session start
  • ➖ Requires design decisions (checkbox vs modal) and analytics/telemetry updates
2. Store safety/benefit copy in a CMS/remote config
  • ➕ Content updates without app redeploys
  • ➕ Easier to iterate on medical/legal copy
  • ➖ More infra/operational complexity (versioning, localization, review workflows)
  • ➖ Increased risk of client/server content drift if not tightly controlled
3. Versioned technique contract with server-side enforcement
  • ➕ Server can require a minimum safety schema/version before allowing session logging
  • ➕ Easier to evolve safety requirements safely over time
  • ➖ More API surface area and migration overhead
  • ➖ Potentially blocks older clients unless backward-compat is maintained

Recommendation: The PR’s approach (server-owned safety + optional, evidence-bound possibleBenefit fields consumed by both clients) is the best baseline because it keeps medically sensitive wording consistent across platforms and testable at the source. Consider adding an explicit acknowledgment gate later if product/legal wants stronger assurance that safety guidance was actively accepted, but the current “always rendered before Start” implementation is a reasonable first step with low user friction.

Files changed (11) +898 / -22

Enhancement (4) +482 / -21
recovery.tsxAdd Breathwork navigation entry to Recovery +10/-0

Add Breathwork navigation entry to Recovery

• Adds a new Recovery navigation link that routes users to the Breathwork screen via expo-router.

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

breathwork.tsxImplement new mobile Breathwork screen with pre-session safety +385/-0

Implement new mobile Breathwork screen with pre-session safety

• Adds a new Breathwork screen that fetches techniques via tRPC, renders technique selection plus safety guidance before starting, runs a timed phase/round loop, and logs completed sessions with retry handling on failure.

packages/mobile/app/breathwork.tsx

breathwork.tsAdd safety guidance + optional evidence-bound benefits to techniques +53/-10

Add safety guidance + optional evidence-bound benefits to techniques

• Extends the BreathworkTechnique model with possibleBenefit and safety guidance, introduces standard and Wim Hof-specific safety constants, and rewrites technique descriptions/benefit wording to avoid categorical health claims.

packages/scoring/src/breathwork.ts

breathwork.tsxRender safety guidance and possible benefits before starting sessions (web) +34/-11

Render safety guidance and possible benefits before starting sessions (web)

• Updates the Breathwork page subtitle and reworks the pre-start UI to include technique description, optional possibleBenefit copy, and a prominent safety section shown before the Start Session button.

packages/web/src/routes/breathwork.tsx

Tests (5) +347 / -1
recovery.test.tsxVerify Recovery navigates to Breathwork route +12/-1

Verify Recovery navigates to Breathwork route

• Refactors expo-router mocking to reuse a shared push spy and adds a test asserting that tapping the Breathwork tool pushes to /breathwork.

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

breathwork.test.tsxAdd BreathworkScreen tests for safety, benefits, flow, and errors +185/-0

Add BreathworkScreen tests for safety, benefits, flow, and errors

• Adds tests ensuring safety guidance and calibrated benefit copy render before Start, Wim Hof warnings are displayed, sessions can be run/logged, and server errors block the start UI.

packages/mobile/app/breathwork.test.tsx

breathwork.test.tsEnforce safety guidance + evidence-bound benefits in technique fixtures +45/-0

Enforce safety guidance + evidence-bound benefits in technique fixtures

• Adds tests requiring safety guidance for every technique, verifying Wim Hof warnings, and ensuring possibleBenefit exists only for techniques supported by the cited trial.

packages/scoring/src/breathwork.test.ts

breathwork.test.tsValidate safety fields are present in breathwork API responses +7/-0

Validate safety fields are present in breathwork API responses

• Updates router tests to assert safety guidance is returned for techniques and that Wim Hof warnings include the loss-of-consciousness warning.

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

breathwork.test.tsxAdd web BreathworkPage tests for safety and possible benefits +98/-0

Add web BreathworkPage tests for safety and possible benefits

• Extends technique fixtures to include safety/possibleBenefit and adds assertions that Wim Hof safety guidance renders before Start and that supported benefits are labeled as possible.

packages/web/src/routes/breathwork.test.tsx

Other (2) +69 / -0
_layout.tsxRegister /breathwork screen in the mobile stack +6/-0

Register /breathwork screen in the mobile stack

• Adds a Stack.Screen entry for the Breathwork route so it’s reachable via navigation and has a title.

packages/mobile/app/_layout.tsx

breathwork.stories.tsxAdd Storybook story for Breathwork safety guidance +63/-0

Add Storybook story for Breathwork safety guidance

• Introduces a Storybook wrapper that mocks the tRPC techniques query with shared TECHNIQUES and renders the Breathwork screen fullscreen.

packages/mobile/app/breathwork.stories.tsx

@github-actions

github-actions Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

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

Channel pr-2243
Deep Link dofek://preview/pr-2243
Commit e0cdf7e

To test on device:

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

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

@github-actions

github-actions Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Storybook previews for e0cdf7e7 are ready:

This comment updates automatically on each PR push.

@qodo-code-review

qodo-code-review Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 194 rules

Grey Divider


Action required

1. Duplicate session timers ✓ Resolved 🐞 Bug ≡ Correctness
Description
BreathworkScreen.startSession can be invoked multiple times before React state flips to isRunning,
and it starts a new interval without guarding/clearing an existing timerRef. This can leave orphan
intervals running and can trigger duplicate logSession mutations (double session logs).
Code

packages/mobile/app/breathwork.tsx[R59-116]

+  const startSession = useCallback(() => {
+    if (!selectedTechnique) return;
+
+    setIsRunning(true);
+    setCurrentRound(1);
+    setCurrentPhase("inhale");
+    startTimeRef.current = new Date().toISOString();
+
+    const phases: { phase: SessionPhase; duration: number }[] = [
+      { phase: "inhale", duration: selectedTechnique.inhaleSeconds },
+    ];
+    if (selectedTechnique.holdInSeconds) {
+      phases.push({ phase: "hold-in", duration: selectedTechnique.holdInSeconds });
+    }
+    phases.push({ phase: "exhale", duration: selectedTechnique.exhaleSeconds });
+    if (selectedTechnique.holdOutSeconds) {
+      phases.push({ phase: "hold-out", duration: selectedTechnique.holdOutSeconds });
+    }
+
+    let round = 1;
+    let phaseIndex = 0;
+    let phaseElapsedMs = 0;
+
+    timerRef.current = setInterval(() => {
+      const phase = phases[phaseIndex];
+      if (!phase) return;
+
+      phaseElapsedMs += 50;
+      setCurrentPhase(phase.phase);
+
+      if (phaseElapsedMs < phase.duration * 1_000) return;
+
+      phaseIndex++;
+      phaseElapsedMs = 0;
+      if (phaseIndex < phases.length) return;
+
+      phaseIndex = 0;
+      round++;
+      if (round <= selectedTechnique.defaultRounds) {
+        setCurrentRound(round);
+        return;
+      }
+
+      if (timerRef.current) {
+        clearInterval(timerRef.current);
+        timerRef.current = null;
+      }
+      setIsRunning(false);
+
+      const completedSession = {
+        techniqueId: selectedTechnique.id,
+        rounds: selectedTechnique.defaultRounds,
+        durationSeconds: totalSessionSeconds(selectedTechnique, selectedTechnique.defaultRounds),
+        startedAt: startTimeRef.current ?? new Date().toISOString(),
+      };
+      setPendingSession(completedSession);
+      logMutation.mutate(completedSession);
+    }, 50);
Relevance

⭐⭐⭐ High

Deterministic correctness bug; team has accepted timer cleanup/guard patterns to prevent duplicate
callbacks and leaks.

PR-#883

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The mobile screen assigns timerRef.current = setInterval(...) unconditionally, so repeated
invocations overwrite the ref and can orphan the previously-created interval. The Start button is
only hidden after state updates, so rapid taps can call startSession multiple times.

packages/mobile/app/breathwork.tsx[59-116]
packages/mobile/app/breathwork.tsx[210-221]

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

### Issue description
`startSession` creates a new `setInterval` without checking whether a session timer is already running. Rapid repeated taps can start multiple intervals, leading to inconsistent UI state and duplicate `logSession` calls.

### Issue Context
This is a new screen (`packages/mobile/app/breathwork.tsx`). React state updates (`setIsRunning(true)`) do not synchronously prevent the handler from being called again before the next render.

### Fix Focus Areas
- packages/mobile/app/breathwork.tsx[59-116]

### Suggested fix
- Add an early guard at the top of `startSession`, e.g. `if (timerRef.current) return;` (or clear any existing interval before creating a new one).
- Optionally also guard on `pendingSession` to prevent starting while a save is pending.
- Ensure `stopSession` + unmount cleanup still clear the interval reliably.

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



Remediation recommended

2. Story tRPC ops not mocked ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The Breathwork Storybook link returns {data: null} for any operation except
breathwork.techniques, so unmocked operations (notably breathwork.logSession) can appear to
succeed and hide broken/missing story setup. This reduces story reliability and can mask
regressions.
Code

packages/mobile/app/breathwork.stories.tsx[R12-25]

+function createMockLink(): TRPCLink<AppRouter> {
+  return () =>
+    ({ op }) => {
+      const result: OperationResultObservable<AppRouter, unknown> = {
+        subscribe(observer) {
+          observer.next?.({
+            result: { data: op.path === "breathwork.techniques" ? TECHNIQUES : null },
+          });
+          observer.complete?.();
+          return { unsubscribe() {} };
+        },
+      };
+      return result;
+    };
Relevance

⭐⭐⭐ High

Team precedent: Storybook tRPC mocks should fail fast on unhandled op paths; null fallbacks were
fixed before.

PR-#2051
PR-#2216

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new Breathwork story explicitly returns TECHNIQUES only for breathwork.techniques and returns
null for everything else. Existing stories in the repo demonstrate the established pattern: throw
on unhandled operation paths to avoid silently-misconfigured stories; this exact pitfall has been
accepted as a recurring bug pattern in prior PRs.

packages/mobile/app/breathwork.stories.tsx[12-25]
packages/mobile/app/cycle.stories.tsx[54-91]
packages/mobile/app/(tabs)/activities.stories.tsx[203-232]
PR-#2216
PR-#2051

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 Storybook tRPC mock returns a successful `null` for unhandled operation paths, which can make stories look correct even when key queries/mutations are not mocked.

### Issue Context
Other Storybook mocks in this repo throw on unhandled paths to avoid silent false-positives.

### Fix Focus Areas
- packages/mobile/app/breathwork.stories.tsx[12-25]

### Suggested fix
- Replace the `: null` fallback with a fail-fast branch (e.g. `throw new Error(...)` including `op.path`).
- Add explicit handling for operations used by the page, at minimum:
 - `breathwork.techniques` (already)
 - `breathwork.logSession` (return a realistic session detail payload or a deterministic stub)
- Keep the observable shape consistent with other stories (including a `pipe()` method if your other story observables implement it).

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


3. Excessive 50ms timer callbacks ✓ Resolved 🐞 Bug ➹ Performance
Description
BreathworkScreen runs a 50ms interval for the full session and calls setCurrentPhase on every
tick, even though phase changes only happen at second-level boundaries. This creates unnecessary JS
timer callback churn during longer sessions (e.g., Wim Hof defaults to ~120 seconds).
Code

packages/mobile/app/breathwork.tsx[R82-90]

+    timerRef.current = setInterval(() => {
+      const phase = phases[phaseIndex];
+      if (!phase) return;
+
+      phaseElapsedMs += 50;
+      setCurrentPhase(phase.phase);
+
+      if (phaseElapsedMs < phase.duration * 1_000) return;
+
Relevance

⭐⭐ Medium

Performance tuning (50ms tick vs coarser updates) is subjective; no close repo precedent found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The breathwork session loop increments elapsed time by 50ms and updates state on each tick. The
scoring technique defaults show Wim Hof runs for ~120 seconds, making this a large number of
callbacks for a UI that only displays discrete phase labels.

packages/mobile/app/breathwork.tsx[67-116]
packages/scoring/src/breathwork.ts[108-116]

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 mobile breathwork session loop uses a 50ms `setInterval` and runs for the full session duration. The UI only needs to change when the phase transitions, so this frequency is higher than needed.

### Issue Context
Wim Hof defaults to 30 rounds with 2s inhale + 2s exhale (~120s), which means ~2400 interval callbacks per session.

### Fix Focus Areas
- packages/mobile/app/breathwork.tsx[67-116]

### Suggested fix
Pick one:
- Replace the 50ms polling interval with `setTimeout` scheduling at phase boundaries (update state once per boundary).
- Or keep an interval but increase it substantially (e.g. 250ms/500ms) and only call `setCurrentPhase` when the phase value actually changes.

Ensure the completion logic and `logSession` call behavior remain unchanged.

ⓘ 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/mobile/app/breathwork.tsx Outdated
Comment thread packages/mobile/app/breathwork.stories.tsx
Comment thread packages/mobile/app/breathwork.tsx Outdated
The guided timer omits the retention and recovery phases of the branded protocol, so present its actual fixed cadence under a neutral name. Keep the evidence DOI while avoiding a one-off dictionary entry for the cited author surname.
@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 added 2 commits July 27, 2026 23:24
Guard starts synchronously so duplicate presses cannot orphan timers or logs. Schedule only at phase boundaries and make Storybook's tRPC operations explicit and fail-fast.
@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 d9a65db into main Jul 28, 2026
103 checks passed
@Asherlc
Asherlc deleted the codex/issue-2066 branch July 28, 2026 13:05
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.

[P0-08] Breathwork claims overstated; breath-hold missing critical safety warnings

2 participants