Skip to content

STUDYU-95: feat(app): defer Fitbit sync while offline - #955

Open
johannesvedder wants to merge 3 commits into
fix/studyu-906-offline-safetyfrom
feat/studyu-906-deferred-fitbit-sync
Open

johannesvedder wants to merge 3 commits into
fix/studyu-906-offline-safetyfrom
feat/studyu-906-deferred-fitbit-sync

Conversation

@johannesvedder

@johannesvedder johannesvedder commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

Description

Related Jira issue: STUDYU-95

Stack 2 of 3 · Previous: #954

Fitbit questionnaire completion previously required immediate connectivity. A participant who completed a Fitbit task offline had no durable acquisition request carrying the original measurement window, so historical Fitbit data could not be safely retrieved after reconnecting or after the study ended.

This PR:

  • persists deferred Fitbit requests before updating the subject cache;
  • preserves each question's original acquisition window across reloads and study completion;
  • uploads pending media before resolving Fitbit requests and ordinary progress;
  • retrieves real Fitbit data after reconnecting instead of uploading placeholders;
  • retains failed requests for retry and removes them only after progress is persisted;
  • prevents duplicate progress after ambiguous or response-loss retries;
  • recovers queued Fitbit work even when its preceding subject-cache write failed;
  • keeps destructive actions blocked while deferred Fitbit work remains pending.

This PR is stacked on #954. The next PR connects the deferred engine to participant kickoff and Fitbit task completion.

Testing Steps

  1. Queue a Fitbit request while offline, reload, reconnect, and verify it resolves once.
  2. Reconnect after the study end date and verify the stored historical window is acquired and uploaded.
  3. Simulate Fitbit acquisition, blob upload, progress upload, and subject-cache failures; verify pending work remains retryable.
  4. Simulate a lost response after remote progress persistence and verify no duplicate progress is created.
  5. Automated verification completed:
    • 48 focused recovery, cleanup, cache, and deferred-Fitbit tests;
    • scripts/pre-commit-check;
    • git diff --check.

PR Checklist

  • I tested the changes and affected user flows.
  • I reviewed the full diff and checked for unintended changes.

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3dfb2092-6e26-461b-a256-22332d910060

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ✅

954 - PR Code Verified

Compliant requirements:

  • Persist deferred Fitbit acquisition requests before updating the subject cache.
  • Preserve each question's original acquisition window across reloads and study completion.
  • Upload pending media before resolving deferred Fitbit requests and ordinary progress.
  • Retrieve real Fitbit data after reconnecting instead of uploading placeholders.
  • Retain failed requests for retry and remove them only after progress is persisted.
  • Avoid duplicate progress after ambiguous or response-loss retries.
  • Recover queued Fitbit work when the preceding subject-cache write failed.
  • Keep destructive actions blocked while deferred Fitbit work remains pending.

Requires further human verification:

  • Verify Fitbit authorization, historical acquisition, and retry behavior on supported mobile platforms with real Fitbit credentials.
  • Verify the complete participant kickoff and task-completion integration, which is explicitly deferred to the next stacked PR.
⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🏅 Score: 82
🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

issue (blocking): broad placeholder detection can drop completed Fitbit progress

_isDeferredFitbitPlaceholder classifies every questionnaire progress entry containing an empty Fitbit response as a deferred placeholder, without checking whether a matching deferred request exists. A completed Fitbit questionnaire can legitimately contain no measurements and therefore an empty list; when that entry is present locally but absent remotely and no deferred request is queued, _mergeProgress skips it, so the completion is never uploaded and can disappear during synchronization. Determine placeholder status from the persisted request identity rather than from the answer payload alone.

static bool _isDeferredFitbitPlaceholder(
  StudySubject subject,
  SubjectProgress progress,
) {
  if (progress.resultType != 'QuestionnaireState') return false;
  final tasks = <Task>[
    ...subject.selectedInterventions.expand(
      (intervention) => intervention.tasks,
    ),
    ...subject.study.observations,
  ];
  final task = tasks.where((task) => task.id == progress.taskId).firstOrNull;
  if (task is! QuestionnaireTask) return false;
  final state = (progress.result as Result<QuestionnaireState>).result;
  return task.questions.questions.whereType<FitbitQuestion>().any((question) {
    final response = state.answers[question.id]?.response;
    return response is List && response.isEmpty;
  });

@github-actions

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 3890916):

(expires Sat, 05 Sep 2026 11:02:05 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 2149dad49ed83535217e50d5c18c0c8c90da629b

@johannesvedder johannesvedder changed the title STUDYU-906: feat(app): defer Fitbit sync while offline STUDYU-95: feat(app): defer Fitbit sync while offline Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant