Skip to main content
Synced from the repo — do not edit here

Canonical source: docs/claude/app-redesign-activity-execution-build-plan.md. This page is generated by docs/scripts/sync-handbook.mjs. Edit the source file in the repo; changes appear here on the next build.

Activity Execution — Implementation Map

Build plan for the redesign per-type activity execution surfaces + states (Figma file 2Vs1e9IZVQh8KpMxc0W3PT, frames Activity · Medication/Supplement/Meal/Strength + Meal · Logged, Workout · Auto-completed, Timed · Active/Complete). Grounded against real basishybrid code. Legend: ✅ exists/reuse · 🔶 net-new · ⚠️ data-integrity prerequisite.


0. Architecture decision

Keep the data + completion layer, replace the presentation. The current detail modal (showBasisModalSummaryWidgetModalSummaryV2, lib/view/routes/summary/route_summary.dart) already:

  • loads all four assigned presets onto the event (_loadRecurringEventPresets, ~:1032),
  • delegates to per-type sections that carry the real completion writes (~:1642-1682),
  • renders the HR time-series graph + zones + hypnogram (WidgetModalSummaryGraph, :532).

So we do not rebuild logic. Two viable shells:

  • (A) Restyle-in-place — reskin WidgetModalSummaryV2 sections under kFeatureRedesignedHome, fix the modal theme (it currently renders light — see below). Lowest new-code, but the file is 2.4k lines with a duplicated client/coach loader (~:1050-1350 and ~:1750-1975).
  • (B) New redesign route rp_activity_detail.dart — reuse the preset-loader (extract _loadRecurringEventPresets into a shared helper) + call the same event-write handlers, rendered with AppTokens. Cleaner separation, matches the rp_* pattern.

Recommendation: (B), with the loader extracted to a shared ActivityPresetResolver so both the legacy modal and the redesign route call one code path (also fixes the client/coach duplication). Entry point stays rpOpenEventSummary(context, event) (already wired in rp_live.dart / rp_plan_live.dart) — repoint it from showBasisModalSummary to the new route.

Theme note: the reused legacy modal renders light because its content has no Material/Scaffold to turn the theme's textTheme into a DefaultTextStyle. A new redesign route sidesteps this entirely (it's a themed Scaffold).


1. Shared components (build once, used by every type + state)

ComponentData sourceStatus
RpActivityShell — status bar + header (back, title, •••) + protocol-progress bar + bottom CTA slot🔶 (chrome; trivial)
RpProtocolProgressBar — "Day N of M" + bar, shown only when protocol has an end dateprotocol doc assignedAt/scheduledFromscheduledUntil/endDate (already read in widget_protocol_detailed.dart _scheduleRange/_computeWeekProgress :157)✅ compute exists · 🔶 UI
RpCadenceStrip — weekday circles (done/today/due/off) + streak textscheduled weekdays = BasisRecurringEventV1.rule.byWeekDays (event_recurring.dart); streak = ServiceStreak RecurringStreakInfo (service_streak.dart:180); "N of M this week" = completed scheduled events this week ÷ scheduled-count✅ rule + streak exist · 🔶 UI + this-week aggregation
RpActivityCadence model{scheduledDays[], todayIndex, weekStates[], streakCount, doneThisWeek, dueThisWeek}derived from rule + ServiceRecurringEvents.determineCompletionStats (service_recurring_events.dart:1420)🔶 thin derive layer

The cadence strip is the answer to "streak isn't only daily": it reads the activity's own recurrence (byWeekDays), so peptide Mon/Thu/Sun, supplement daily, and workout 4×/wk each render their real schedule. Put it on every activity + every state.


2. Data foundations / prerequisites (do FIRST — the UI lies without them)

#PrerequisiteWhereStatus
P0-a ⚠️Medication onto the event model. Today per-dose "Taken" writes to an ad-hoc events/{id}.medicationCompletions map (widget_section_medication.dart:253-298), invisible to determineCompletionStats + ServiceStreak + instance history. Add a structured event.medications: List<BasisMedicationV1> (mirror event.supplements) with per-med completionState; migrate the toggle to write it; keep back-compat read of the old map.basiscore event model + functions writer + widget_section_medication.dart🔶 model+backend+client
P0-b 🔶Meal target macros on the assigned preset. AssignedMealPreset (widget_section_medication.dart:6) is name/imageUrl/description only. Add targetNutrients {calories,protein,carbs,fat} so "logged vs target" is real.AssignedMealPreset model + protocol assignment writer (basisflow-web/functions)🔶 model+backend+assign UI
P0-c 🔶Medication video field. Add videoUrl/videoRef to the assigned medication (mirror BasisExerciseV1.videoUrl, exercise.dart:89) + a place to set it in protocol assignment.AssignedMedication model + assign UI🔶 model+assign UI
P0-dProtocol progress needs no new data — assignedAt/scheduledUntil/endDate already on protocol docs.

3. Per-type detail — element → code map

💊 Medication (Activity · Medication)

  • Assigned dataAssignedMedication{name,dosage,unit,schedule,timing,brandName,notes} (widget_section_medication.dart:27), loaded via _loadRecurringEventPresets.
  • Dose-prominent + route/timing chips ✅ all fields present.
  • "Why" linenotes (or protocol rationale). Skip · reason 🔶 new sheet → write a skip+reason onto the med completion (needs P0-a model).
  • Per-dose video 🔶 needs P0-c; player reuse ✅ (same as exercise video modal).
  • Taken toggle ⚠️ rewrite to P0-a structured write (not the off-model map).
  • CTA "Mark all as taken" → sets each med completed + rolls event.completionStatus=completed (pattern exists :287), fires ServiceStreak.updateStreaks.

🧪 Supplement (Activity · Supplement)

  • Flat list, timing-in-name ✅ just render event.supplements / assigned preset flat (drop AM/PM grouping + count stats).
  • Per-item provider noteBasisSupplementV1.units[].annotation + suggestedUse (supplement.dart) already carry it.
  • Per-item check_onCompleteSupplement (widget_section_supplement.dart:202) toggles completionState on event.supplements; auto-rolls event complete. Reuse as-is.

🍽️ Meal (Activity · Meal)

  • Recommended-meal heroAssignedMealPreset name/image/description (real assignment).
  • TARGET macros 🔶 needs P0-b.
  • Scan/search log CTAs ✅ route to RouteMealScan (Open Food Facts + analyze_meal); actual writes to event.meals (BasisMealV1 w/ full nutrients) via WidgetSummarySectionFood (widget_section_food.dart:740).

🏋️ Exercise (Activity · Strength) — mostly reuse

  • Per-set weight×reps loggingWidgetInlineExerciseEditor (widget_section_exercise.dart:50-256) → event.exercises.
  • Per-exercise complete + auto-roll-up_onCompleteExercise (:537), _onCompletePresetExercise (:588).
  • Video ✅ field exists (BasisExerciseV1.videoUrl); populate URLs 🔶.
  • Rest timer / last-session reference 🔶 net-new UI (data: prior event.exercises).
  • Work here is reskin only (+ the two 🔶 additions).

4. States — element → code map

Meal · Logged (post-scan)

  • Scanned photo + detected items ✅ from RouteMealScan result (event.meals[].images, .items).
  • Logged-vs-target macro bars ✅ logged nutrients (SummaryObjectMealNutrientsV1, real) vs target (needs P0-b). 🔶 bar UI.
  • Atlas on-target note 🔶 consumer-Atlas call (optional; can be a simple rule first).

Workout · Auto-completed

  • Auto-complete ✅ wearable-detected summary events already flow in (Terra/HealthKit → BasisEventV1 summary); auto-complete when matched to a scheduled activity 🔶 (matching logic).
  • Session HR graph + zonesreuse WidgetModalSummaryGraph (route_summary.dart:532) — it already renders WidgetGraphData (HR time-series) + WidgetZoneSection (zones). Just skin.
  • Stats (dur/dist/HR/kcal)keyStats on the summary.
  • "Counts toward plan · N of M this week" 🔶 = plan-match + cadence (shares RpCadenceStrip).

Timed · Active / Complete (fasting / sauna)

  • Ring timer ✅ timer state from ServiceTimer / BasisDurationTimer (TimerType.event; used in widget_section_events.dart:154). 🔶 ring UI.
  • Start/pause/end ✅ existing timer controls (showBasisModalTimer, time(event) in SummaryActions).
  • Complete summary + streakevent.completionStatus=completed + ServiceStreak; 🔶 ring/summary UI.

5. Sequencing (phased, dependency-ordered)

Phase 0 — data foundations (backend + model; unblocks honest UI). P0-a medication-on-model (highest value), P0-b meal target macros, P0-c med video field. Each = model field + functions writer + (b,c) assignment UI. Deploy via ship.sh.

Phase 1 — shared redesign shell + components. RpActivityShell, RpProtocolProgressBar, RpCadenceStrip (+ RpActivityCadence derive). Extract ActivityPresetResolver from _loadRecurringEventPresets (kills the client/coach dup). Repoint rpOpenEventSummary → new rp_activity_detail.dart route.

Phase 2 — per-type detail (reuse handlers). Supplement (pure reuse + reskin) → Meal (hero + target, needs P0-b) → Medication (reskin + video + P0-a taken-write + skip-reason) → Strength (reskin + rest timer/last-session).

Phase 3 — states. Workout · Auto-completed (reuse WidgetModalSummaryGraph + plan-match) → Meal · Logged (reuse scan + macro compare) → Timed Active/Complete (reuse ServiceTimer).

Phase 4 — verify on device. Real account (axuk-khwf-prkr, uid GoOU3d2QcyRZMD83IhbQeIclpwh2), each type end-to-end (assign in basisflow-web → execute in app → confirm completion + streak + adherence reflect it). Vision/video best tested on a physical device.


6. What's genuinely new vs reuse (headline)

  • Reuse (already coded): per-set exercise logging, supplement completion, meal scan + nutrients, HR graph + zones + hypnogram, timer service, streak service, week/adherence compute, protocol progress compute, biomarker baseline→current.
  • Net-new (build): medication-on-model (⚠️ + unblocks adherence), meal target macros, med video field + player wiring, RpCadenceStrip (schedule-aware) + protocol-progress UI, the redesign presentation layer, plan-match for auto-complete, rest-timer/last-session reference.
  • The one trap: medication adherence/streak is untruthful until P0-a lands — don't ship the med streak before it.

6b. Workout planned↔wearable merge — VERIFIED model (build on this)

Link-and-union, NOT a physical merge. User-logged data is safe by construction.

  • Planned strength habit is seeded from workoutPresetevent.exercises at schedule time (service_recurring_events.dart:843-855). Target and actual share ONE list; the user edits rows in place via updateEventForUser (read-modify-write; writes only touched fields) — widget_section_exercise.dart:617-653.
  • Wearable session = separate BasisSummaryV1 (HR/cal/duration) in a different collection. On landing, client-side auto-complete (service_daymemory.dart:1062-1071) matches nearest unknown-status habit of same activity-family within 10h window (service_recurring_events.dart:692-736) and stamps completionStatus=autoCompleted + autocompletedBy=summaryId + wearable start/end/deviceInfo — never touches exercises.
  • Detail renders the union: fetch summary via autocompletedBy for the HR graph (route_summary.dart:147-177) while the event keeps logged sets.
  • Safety (3 guarantees): RMW write only mutates time/status/link/device; auto-complete gated to unknown-status only (won't re-process completed/edited); summaries are a separate table with no set-without-merge on the event doc.
  • Caveats: auto-complete overwrites the event's TIME WINDOW (not sets); no per-set notes/RPE field; no edit-preservation flags (safety is structural). The "canonical record + per-field provenance" in app-redesign-data-contracts.md §1 is aspirational, not built — and not needed for this build.
  • Build implication: Strength + Workout·Auto detail just render event.exercises + fetch autocompletedBy summary. No new merge engine.

6c. Added activity types/states (Figma)

  • Activity · Measurement (121:825) — value-entry rows (weight/waist/hip/body-fat) + last value + trend link; feeds metric graphs + biomarker progress. NEW type for body measurements in protocols.
  • Activity · Meditation (122:825) — session-DB picker (recommended + options; net-new meditation DB, same shape as meal templates) + Start-session timer + mark-done-without-timer.
  • Timed · Active now offers mark-done-without-timer (exists in current code) alongside the ring timer.
  • Meal · Logged detected items are editable rows (name/qty/calories + Add item) — manual edit already exists (widget_section_food.dart _editQuantity/_editNutrient), preserve alongside AI edit.

7. Key file anchors

  • Entry/modal: lib/view/routes/summary/route_summary.dart (WidgetModalSummaryV2, _loadRecurringEventPresets, WidgetModalSummaryGraph:532)
  • Sections: widget_section_exercise.dart (WidgetInlineExerciseEditor:50, _onCompleteExercise:537), widget_section_supplement.dart (_onCompleteSupplement:202), widget_section_medication.dart (AssignedMedication:27, AssignedMealPreset:6, _onCompleteMedication:253), widget_section_food.dart (:740)
  • Completion mixin: route_summary_actions.dart (complete/skip/undo, fires ServiceStreak)
  • Streak: services/service_streak.dart:180 · Adherence/week: service_recurring_events.dart:1420, widget_protocol_detailed.dart:157
  • Biomarker: widget_biomarker_progress.dart (from healthSummaries)
  • Models: basiscore/.../event/exercise_preset.dart, exercise.dart:89 (videoUrl), supplement.dart, event/event_recurring.dart (rule.byWeekDays), event.dart (exercises/supplements/meals/*Preset maps)
  • Redesign entry already wired: rp_live.dart rpOpenEventSummary, rp_plan_live.dart _eventRow