Skip to content

Macro and micronutrient tracking

Macro and micronutrient tracking turns prescribed nutrition into auditable daily intake facts.

Tags: Nutrition, Target sets, Food facts, Offline logs, Coach review

Area: Nutrition / DailyExecution / ProgressAnalytics

The V1 baseline stores coach-prescribed nutrient targets as versioned target sets, lets clients confirm planned meals or log deviations, calculates daily macro and coach-selected micronutrient totals, and exposes variance, adherence, hydration, and supplement signals to Today view, coach review, and progress analytics.

Boundary decision

Nutrition owns target sets, plan versions, food facts, intake logs, supplement schedules, hydration logs, and nutrient calculations. DailyExecution owns dated Today task instances and task status. ProgressAnalytics consumes accepted nutrition events and aggregate rows for charts, adherence, flags, and recaps.

Sync boundary

Nutrition keeps target, meal, hydration, supplement, and nutrient business rules local. Shared mobile sync rules for device ids, client operation ids, receipts, replay, conflict handling, and local cache pruning follow the Offline Sync Protocol.

Tracking Flows

The flow supports structured meal plans, macro-only coaching, and hybrid plans without hardcoding one diet method.

Coach Defines Targets

  1. Coach opens a client relationship and creates a target set with effective dates, timezone, day type rules, and tracked nutrients.
  2. Common macro targets are seeded: energy, protein, carbohydrate, fat, fiber, sugar, sodium, and water.
  3. Coach can enable specific vitamins, minerals, electrolytes, or custom nutrients only when useful for that client.
  4. Targets support exact values, minimums, maximums, ranges, tolerance bands, and optional client visibility labels.
  5. Publishing a target set emits an event that refreshes future nutrition tasks in Today view.

Coach Assigns A Plan

  1. Coach can assign a meal plan version with meal slots, planned foods, serving quantities, alternatives, supplement schedule, hydration goal, and notes.
  2. A plan can be fixed meals, macro targets only, or a hybrid where some meals are prescribed and others are free tracking.
  3. Training-day, rest-day, high-carb, low-carb, refeed, fasting, and custom day types share the same target-set and plan-version model.
  4. Publishing a new plan supersedes future pending tasks but does not rewrite completed historical logs.

Client Logs Intake

  1. Client opens Today and sees meal, hydration, supplement, or macro-check tasks derived from the active nutrition plan snapshot.
  2. For a prescribed meal, the client can complete as planned, adjust portions, skip foods, select approved alternatives, or add unplanned foods.
  3. For macro-only tracking, the client can log foods, quantities, water, and notes; free-form manual macro totals are coach-only in Public V1.
  4. Mobile writes a local draft first, calculates visible totals from cached food facts, and queues sync with stable client-generated ids.
  5. Backend recalculates canonical totals after sync and returns accepted aggregates, warnings, and any source-confidence notes.

Coach Reviews Variance

  1. Daily aggregates compare accepted intake against the target set active for that local day and day type.
  2. Coach sees macro variance, hydration, missed supplements, repeated under-protein days, extreme calorie swings, and coach-selected micronutrient issues.
  3. Coach can review a day, message the client, adjust future targets, request clarification, or create a follow-up task.
  4. Weekly recap jobs summarize trends without treating food database values as medical certainty.

Domain Model

Nutrition uses immutable prescription versions plus append-friendly intake facts so history remains explainable.

Aggregates & Entities

  • NutrientDefinition: system or coach-owned nutrient catalog row with code, category, unit, precision, and source metadata.
  • FoodItem: system seed, workspace/coach, client-visible, or future provider food record with source, locale, barcode, serving units, and nutrient facts.
  • NutritionTargetSet: aggregate root for relationship-scoped targets, effective dates, day type mappings, tracked nutrients, and tolerance policy.
  • NutritionPlanVersion: immutable published nutrition prescription with meal slots, planned food portions, alternatives, notes, windows, and supplement schedule links.
  • DailyNutritionLog: aggregate root for one relationship and local day, including day type, plan snapshot reference, status, revision, and aggregate checksum.
  • MealIntakeLog and FoodIntakeLine: meal-level completion state plus accepted planned, swapped, adjusted, skipped, or unplanned food lines.
  • NutrientDailyAggregate: calculated macro and tracked micronutrient totals with target variance and source confidence metadata.
  • SupplementScheduleItem, SupplementIntakeLog, and HydrationLog: adherence facts adjacent to food nutrients, not hidden inside food totals.

Value Objects & Rules

  • LocalDay: date plus IANA timezone; nutrition logs are anchored to the relationship timezone unless the coach changes policy.
  • NutrientAmount: nutrient id, decimal amount, unit, precision, and source; never store nutrient values as free-form strings.
  • TargetRange: exact target, minimum, maximum, warning tolerance, success tolerance, and display behavior.
  • ServingQuantity: numeric quantity, serving unit, gram equivalent when known, and conversion confidence.
  • MealLogStatus: PLANNED, COMPLETED_AS_PLANNED, ADJUSTED, SKIPPED, PARTIAL, UNPLANNED.
  • NutrientSource: system seed, coach-created, client-entered food, coach manual macro, imported PDF draft, or future provider source.
  • Food nutrient facts are calculation inputs. Coach-facing views should expose source and confidence for precise micronutrients.
  • Changing a food item later must not alter accepted historical logs; intake lines store calculation snapshots for replay and audit.
  • Supplements can include nutrient metadata, but supplement adherence is tracked separately from food intake totals unless the schedule item is a coach-marked nutrition-bearing supplement such as protein powder or a meal replacement.
ModelOwned ByImportant MethodsEmits
NutritionTargetSetNutritionpublish(), supersede(), resolveForDayType(), trackNutrient(), setTolerance()NutritionTargetSetPublished, NutritionTargetsSuperseded
NutritionPlanVersionNutritionpublish(), addMealSlot(), addPlannedFood(), addAlternative(), attachTargets()NutritionPlanPublished, NutritionPlanSuperseded
DailyNutritionLogNutritionstart(), logMeal(), adjustFood(), skipMeal(), submit(), correct()NutritionLogSubmitted, NutritionLogCorrected
NutrientDailyAggregateNutrition projectioncalculate(), compareToTargets(), markStale(), rebuildFromLog()NutritionDailyAggregateUpdated
SupplementIntakeLogNutritioncomplete(), skip(), recordDose(), requestClarification()SupplementIntakeRecorded

Table Structure

Persist catalog and prescription data separately from accepted daily facts and calculated projections.

TablePurposeImportant ColumnsConstraints & Indexes
nutrient_definitionsCanonical nutrient catalog for macros, micronutrients, water, and coach-defined tracked nutrients.id, workspace_id, code, display_name, category, unit, precision, source, is_system, archived_atUnique active (workspace_id, code); seed system nutrients globally; archive rather than delete when referenced.
food_itemsSystem seed, workspace/coach-created, client-visible, and future provider foods used in plans and logs.id, workspace_id, provider, provider_food_id, name, brand, barcode, locale, source_confidence, created_by_user_id, archived_atIndex barcode and optional provider id; coach-created foods are workspace-scoped; future provider rows can be refreshed without rewriting logs.
food_serving_unitsServing and conversion options for a food item.id, food_item_id, label, quantity, gram_weight, is_default, confidenceUnique label per food item; gram conversion can be null for coach-authored manual macro lines.
food_nutrient_factsNutrient values for foods per 100g or serving source.id, food_item_id, nutrient_definition_id, amount, basis, serving_unit_id, source, confidence, verified_atUnique (food_item_id, nutrient_definition_id, basis, serving_unit_id); index nutrient facts by food for calculation.
nutrition_target_setsPublished relationship-scoped target-set versions.id, workspace_id, relationship_id, version, status, effective_from, effective_until, timezone, day_type_policy jsonb, published_at, superseded_atUnique (relationship_id, version); index active by relationship and date; published rows are immutable except lifecycle fields.
nutrition_target_valuesOne nutrient target per target set and day type.id, target_set_id, day_type, nutrient_definition_id, target_amount, min_amount, max_amount, warning_tolerance, success_tolerance, visible_to_clientUnique (target_set_id, day_type, nutrient_definition_id); nullable target/min/max support exact, range, minimum-only, or maximum-only rules.
nutrition_plan_versionsImmutable published meal-plan versions or macro-only prescriptions.id, workspace_id, relationship_id, target_set_id, version, status, plan_mode, effective_from, effective_until, notes_snapshot jsonb, published_atUnique (relationship_id, version); current active plan query by relationship and date.
meal_slot_templatesMeal slots inside a published plan version.id, plan_version_id, slot_key, label, day_type, sort_order, start_time, end_time, instructionsUnique (plan_version_id, slot_key); planned future Today tasks reference slot key.
planned_food_portionsPrescribed food portions in a meal slot.id, meal_slot_template_id, food_item_id, serving_unit_id, quantity, gram_weight_snapshot, nutrient_snapshot jsonb, notes, sort_orderSnapshot nutrient facts at publish time; index by meal slot for rendering.
meal_alternativesCoach-approved swaps for a planned food or meal slot.id, plan_version_id, source_portion_id, meal_slot_template_id, food_item_id, serving_unit_id, quantity, nutrient_snapshot jsonb, notesAlternatives preserve snapshot values and do not need to match macros exactly unless policy requires it.
daily_nutrition_logsOne accepted or in-progress nutrition log per relationship and local day.id, client_log_id, workspace_id, relationship_id, local_date, timezone, day_type, target_set_id, plan_version_id, status, current_revision, submitted_atUnique (relationship_id, local_date); unique nullable (relationship_id, client_log_id); index by date descending.
meal_intake_logsMeal-level status and notes inside a daily log.id, client_meal_log_id, daily_nutrition_log_id, meal_slot_template_id, meal_slot_key, status, client_note, logged_atUnique (daily_nutrition_log_id, client_meal_log_id); index by daily log and slot order.
food_intake_linesAccepted food entries, adjusted portions, swaps, skipped items, and unplanned foods.id, client_line_id, meal_intake_log_id, line_type, planned_food_portion_id, food_item_id, serving_unit_id, quantity, gram_weight_snapshot, nutrient_snapshot jsonb, source, confidenceUnique (meal_intake_log_id, client_line_id); line snapshots preserve historical calculations.
nutrient_daily_aggregatesCalculated daily totals and target variance per tracked nutrient.id, daily_nutrition_log_id, nutrient_definition_id, actual_amount, target_amount, min_amount, max_amount, variance_amount, variance_percent, status, calculated_atUnique (daily_nutrition_log_id, nutrient_definition_id); rebuildable from accepted intake lines and target snapshot.
supplement_schedule_itemsPrescribed supplement tasks attached to a plan or target set.id, plan_version_id, label, dose_amount, dose_unit, timing_hint, meal_relation, day_type, notesItems such as protein powder, vitamin D, omega-3, creatine, zinc, and magnesium are rows, not schema columns; only coach-marked nutrition-bearing items count toward totals.
supplement_intake_logsDaily completion facts for supplement schedule items.id, client_intake_id, daily_nutrition_log_id, schedule_item_id, status, dose_taken, taken_at, skip_reason, noteUnique (daily_nutrition_log_id, schedule_item_id, client_intake_id); feeds adherence by default and nutrient totals only when the supplement calculation rule enables it.
hydration_logsWater intake events and daily hydration totals.id, client_entry_id, daily_nutrition_log_id, amount_ml, source, logged_atUnique (daily_nutrition_log_id, client_entry_id); roll up to water target variance.
mobile_nutrition_sync_receiptsServer receipt for idempotent mobile nutrition operations.id, relationship_id, device_id, client_operation_id, operation_type, canonical_record_id, accepted_revision, payload_checksum, accepted_atUnique (relationship_id, device_id, client_operation_id); supports retry-safe offline logging.

Implementation note

SQLite mirrors only active target sets, compact plan snapshots, meal slots, planned portions, approved alternatives, referenced/recent food facts, local daily drafts, hydration events, supplement events, and nutrition outbox receipts. Default prefetch is today plus the next 7 local days; keep accepted local history for recent review/retry, but never prefetch the full food catalog.

Lifecycle Policy

Prescriptions are versioned, logs are corrected by revision, and source food data can change without rewriting history.

Prescriptions And Catalog

  • Published target sets and plan versions are immutable except for superseded, archived, or retention fields.
  • Coach edits create a draft and then a new published version. Future Today tasks refresh from the new version.
  • Archived foods and nutrients stay available to historical logs and existing plan versions.
  • Future external catalog refreshes update catalog rows but not nutrient_snapshot values stored on planned portions or accepted intake lines.
  • PDF imports start as draft plan data and require coach confirmation before becoming a published plan version.

Daily Logs And Corrections

  • Client edits before submit update the in-progress daily log draft and increment client revision.
  • Accepted submissions create or update canonical meal logs, intake lines, aggregates, and sync receipts transactionally.
  • Corrections create a new accepted revision and rebuild aggregates from current accepted line state.
  • Voided or support-corrected logs remain auditable and are excluded from normal analytics unless explicitly restored.
  • Closed relationships follow CoachClientManagement retention and privacy rules; sensitive food notes are anonymized before hard purge.

Guardrail

Do not infer medical diagnoses from nutrient logs. Risk flags should be coach-configurable review signals, not automated clinical claims.

API Contracts

Coach endpoints publish prescriptions; client endpoints fetch snapshots and sync daily facts idempotently.

EndpointPurposeRequestResponse
GET /api/v1/client/nutrition/today?date=YYYY-MM-DDReturn nutrition plan snapshot, targets, meal tasks, supplement tasks, hydration target, and current accepted log state.Local date, timezone, optional snapshot checksum, optional sync cursor.Targets, plan version, meal slots, planned portions, alternatives, current totals, outbox hints, stale-source warnings.
POST /api/v1/client/nutrition/logsStart or resume a daily nutrition log.client_log_id, relationship id, local date, timezone, plan version id, target set id, device id.Canonical daily log id, current revision, accepted meal logs, aggregate summary.
POST /api/v1/client/nutrition/logs/{logId}/mealsLog a meal as planned, adjusted, skipped, swapped, or unplanned.Client operation id, expected revision, meal log id, meal status, food lines, notes, logged at.Accepted meal log, canonical ids, recalculated daily totals, warnings, sync receipt.
POST /api/v1/client/nutrition/logs/{logId}/hydrationAdd or correct a water intake event.Client operation id, amount ml, source, logged at.Hydration event, water total, water target variance, sync receipt.
POST /api/v1/client/nutrition/logs/{logId}/supplementsRecord supplement completion, skip, or partial dose.Client operation id, schedule item id, status, dose taken, taken at, note.Supplement intake log, adherence summary, sync receipt.
POST /api/v1/client/nutrition/logs/{logId}/submitSubmit the daily nutrition log for canonical review.Expected revision, client revision, final meal lines, hydration entries, supplement entries, submitted at, idempotency key.Submitted log dto, daily aggregates, target variance, adherence events, projection refresh hints.
POST /api/v1/client/nutrition/syncBulk sync offline nutrition operations.Device id, ordered operations, client ids, expected revisions, payload checksums.Accepted operations, rejected stale operations, canonical id map, latest aggregates, refresh hints.
GET /api/v1/coach/relationships/{relationshipId}/nutrition/summaryCoach reads daily or weekly nutrient summaries.Date range, day type filter, nutrients, variance status, pagination cursor.Daily summaries, macro totals, tracked micronutrient variance, hydration, supplements, review flags.
POST /api/v1/coach/relationships/{relationshipId}/nutrition/target-setsCreate and publish a target set version.Effective dates, day type policy, target values, visibility, tolerance policy.Published target set, version, materialization hints, validation warnings.
POST /api/v1/coach/relationships/{relationshipId}/nutrition/plansCreate and publish a meal-plan or macro-only plan version.Plan mode, target set id, meal slots, planned portions, alternatives, supplement schedule, notes.Published plan version, snapshot checksum, affected Today window.
GET /api/v1/coach/foods/searchSearch system seed, workspace, client-visible, and enabled external-catalog foods for plan building.Query, barcode, locale, source filter, pagination cursor.Food hits, serving units, common nutrient facts, source confidence.

Key Code Snippets

Implementation sketches show the storage shape, calculation boundary, logging use case, and access policy.

Drizzle schema shape

apps/api/src/nutrition/db/nutrition-tracking.schema.ts

ts
export const nutritionTargetSets = pgTable("nutrition_target_sets", {
  id: uuid("id").primaryKey().defaultRandom(),
  workspaceId: uuid("workspace_id").notNull(),
  relationshipId: uuid("relationship_id").notNull(),
  version: integer("version").notNull(),
  status: text("status").$type<NutritionTargetSetStatus>().notNull(),
  effectiveFrom: date("effective_from").notNull(),
  effectiveUntil: date("effective_until"),
  timezone: text("timezone").notNull(),
  dayTypePolicy: jsonb("day_type_policy").$type<DayTypePolicy>().notNull(),
  publishedAt: timestamp("published_at", { withTimezone: true }),
  supersededAt: timestamp("superseded_at", { withTimezone: true }),
}, (table) => ({
  relationshipVersionUnique: uniqueIndex("nutrition_target_sets_relationship_version_unique")
    .on(table.relationshipId, table.version),
  activeLookupIdx: index("nutrition_target_sets_active_lookup_idx")
    .on(table.relationshipId, table.effectiveFrom, table.status),
}));

export const foodIntakeLines = pgTable("food_intake_lines", {
  id: uuid("id").primaryKey().defaultRandom(),
  clientLineId: uuid("client_line_id").notNull(),
  mealIntakeLogId: uuid("meal_intake_log_id").notNull(),
  lineType: text("line_type").$type<FoodIntakeLineType>().notNull(),
  plannedFoodPortionId: uuid("planned_food_portion_id"),
  foodItemId: uuid("food_item_id"),
  servingUnitId: uuid("serving_unit_id"),
  quantity: numeric("quantity", { precision: 12, scale: 4 }).notNull(),
  gramWeightSnapshot: numeric("gram_weight_snapshot", { precision: 12, scale: 4 }),
  nutrientSnapshot: jsonb("nutrient_snapshot").$type<NutrientSnapshot>().notNull(),
  source: text("source").$type<NutritionSource>().notNull(),
  confidence: text("confidence").$type<SourceConfidence>().notNull(),
}, (table) => ({
  clientLineUnique: uniqueIndex("food_intake_lines_client_line_unique")
    .on(table.mealIntakeLogId, table.clientLineId),
}));

Nutrient calculation service

apps/api/src/nutrition/domain/nutrient-calculator.ts

ts
export class NutrientCalculator {
  calculateDailyAggregate(input: CalculateDailyAggregateInput): DailyNutrientResult[] {
    const tracked = new Set(input.targetSet.trackedNutrientIds());
    const totals = new Map<string, Decimal>();

    for (const line of input.acceptedFoodLines) {
      for (const nutrient of line.nutrientSnapshot.values) {
        if (!tracked.has(nutrient.nutrientDefinitionId)) continue;

        const current = totals.get(nutrient.nutrientDefinitionId) ?? new Decimal(0);
        totals.set(nutrient.nutrientDefinitionId, current.plus(nutrient.amount));
      }
    }

    return input.targetSet.targetsFor(input.dayType).map((target) => {
      const actual = totals.get(target.nutrientDefinitionId) ?? new Decimal(0);
      return DailyNutrientResult.compare({
        nutrientDefinitionId: target.nutrientDefinitionId,
        actual,
        target,
        sourceConfidence: input.sourceConfidenceSummary,
      });
    });
  }
}

Log meal use case

apps/api/src/nutrition/use-cases/log-meal-intake.ts

ts
@Injectable()
export class LogMealIntakeUseCase {
  constructor(
    private readonly logs: DailyNutritionLogRepository,
    private readonly receipts: NutritionSyncReceiptRepository,
    private readonly calculator: NutrientCalculator,
    private readonly policy: NutritionPolicy,
    private readonly tx: TransactionRunner,
    private readonly outbox: OutboxPort,
  ) {}

  async execute(command: LogMealIntakeCommand, actor: Actor): Promise<NutritionLogDto> {
    return this.tx.run(async () => {
      const existing = await this.receipts.findAccepted(command.idempotencyKey());
      if (existing) return this.logs.getDto(existing.dailyNutritionLogId);

      const log = await this.logs.getForUpdate(command.dailyNutritionLogId);
      this.policy.assertClientCanUpdateLog(actor, log);
      log.assertExpectedRevision(command.expectedRevision);

      log.recordMeal(command.toMealIntake(), this.calculator);

      await this.logs.save(log);
      await this.receipts.accept(command.toReceipt(log));
      await this.outbox.publish(log.pullDomainEvents());

      return NutritionLogDto.fromDomain(log);
    });
  }
}

Access policy

apps/api/src/nutrition/policies/nutrition.policy.ts

ts
export class NutritionPolicy {
  assertCoachCanPublishPlan(actor: Actor, relationshipId: string): void {
    actor.requireRole("COACH");
    actor.requireCoachRelationshipAccess(relationshipId);
    actor.requirePermission("nutrition.plan.write");
  }

  assertClientCanUpdateLog(actor: Actor, log: DailyNutritionLog): void {
    actor.requireRole("CLIENT");
    actor.requireRelationshipParticipant(log.relationshipId);
    log.assertClientMutable();
  }

  assertCoachCanReadSummary(actor: Actor, relationshipId: string): void {
    actor.requireRole("COACH");
    actor.requireCoachRelationshipAccess(relationshipId);
  }
}

Events & Background Jobs

Nutrition publishes prescription changes for Today materialization and daily facts for analytics.

Emitted Events

  • NutritionTargetSetPublished: refreshes future macro and hydration target tasks.
  • NutritionPlanPublished: materializes meal, supplement, hydration, and free-tracking tasks for the active date window.
  • NutritionLogSubmitted: updates Today completion, daily adherence facts, coach summaries, and client timeline.
  • NutritionDailyAggregateUpdated: feeds progress charts, variance flags, weekly recaps, and coach task inbox rules.
  • SupplementIntakeRecorded and HydrationLogged: update daily adherence projections and Today counters.

Jobs And Projectors

  • Rolling nutrition task materializer keeps today plus the next 7 local days ready for client mobile prefetch.
  • Nutrient aggregate projector rebuilds daily totals from accepted food lines after submission, correction, or voiding.
  • Variance flag worker detects repeated under-protein days, hydration gaps, missed supplements, and coach-selected micronutrient issues.
  • Food catalog maintenance stores source metadata and queues coach review when a used food has low confidence; external provider import jobs are post-V1.
  • Weekly recap job summarizes target adherence and notable deviations without exposing untracked micronutrients to clients.

Permissions & Access Rules

Nutrition records include sensitive health and behavior data, so every query is relationship-scoped.

Client

  • Client can read active plan snapshots, target values marked visible, their own daily logs, and their own historical summaries.
  • Client can create, edit, submit, and correct logs only until the coach reviews that local day or 24 hours after submission, whichever comes first.
  • Client cannot enable new tracked micronutrients, enter free-form manual macro totals, alter target definitions, or modify published plan snapshots.
  • Client-entered custom foods are visible only according to workspace policy and require coach review before becoming reusable plan foods.

Coach And Support

  • Coach can publish targets and plans only for relationships they manage and workspaces where they have nutrition permissions.
  • Coach can read all nutrition logs for authorized relationships, including source confidence, hidden coach notes, and tracked micronutrient variance.
  • Coach mobile may draft quick adjustments but publishing follows the same validation and versioning policy as web.
  • Support/admin access to raw food logs, sync payloads, and deleted records requires explicit permission and audited reason.

Web, Coach Mobile, Client Mobile

Coach surfaces emphasize prescription and review; client mobile emphasizes fast logging and offline confidence.

Coach Web

  • Build target sets, choose tracked nutrients, set day-type variants, and publish plan versions.
  • Search food catalog, create workspace foods, review source confidence, and inspect nutrient facts before assignment.
  • Review daily and weekly summaries with macro variance, micronutrient flags, hydration, supplements, and meal-level notes.
  • Adjust future targets from review surfaces without mutating historical logs.

Coach Mobile

  • Read compact nutrition summaries, missed supplement flags, hydration gaps, and client notes.
  • Approve simple food substitutions or message the client from review context.
  • Draft target changes for later review when the full plan builder is too dense for mobile.

Client Mobile

  • Today shows meal, water, supplement, and macro-check tasks from SQLite before network refresh.
  • Meal detail supports complete as planned, portion adjustment, approved swaps, unplanned foods, skip reasons, notes, and photo attachment later.
  • Visible totals update locally with cached facts, then reconcile to canonical server totals after sync.
  • Client-visible variance uses simple status bands and remaining/over target hints for visible targets; coach-only severity, confidence notes, and repeated-pattern scoring stay out of the client payload.
  • Offline state must distinguish draft, queued, syncing, accepted, rejected, and needs-review states.
  • Arabic/English labels, RTL, dark mode, dynamic type, and accessible nutrient status text are required for V1 baseline parity.

Test Scenarios & V1 Baseline Decisions

Nutrient tracking needs strong calculation, versioning, offline, and permissions coverage because small errors compound into misleading coaching feedback.

Tests

  • Domain unit tests: target range validation, day-type resolution, serving conversion, nutrient calculation, variance status, and supplement adherence.
  • Versioning tests: published target sets and plan versions are immutable, future tasks refresh after supersede, completed historical logs retain old snapshots.
  • API integration tests: client fetches today's nutrition snapshot, logs adjusted meal, submits once, retries idempotently, and receives canonical aggregates.
  • Offline sync tests: duplicate operation retry, stale revision rejection, accepted canonical id mapping, pending local meal preserved during refresh.
  • Food catalog tests: system/workspace food creation, optional future provider import, barcode lookup, missing gram conversion, low-confidence micronutrient warning.
  • Access tests: client cannot read another relationship's logs, coach cannot publish for unauthorized relationships, support raw payload access is audited.
  • Mobile E2E tests: open Today offline, complete a prescribed meal, add water, mark supplement taken, reconnect, sync once, and update coach summary.
  • Localization and accessibility tests: Arabic/English nutrient labels, RTL layout, dynamic type, screen-reader status labels, dark mode contrast, and long food names.

Answered Decisions

  • No third-party food data provider seeds V1 baseline. Use system nutrient definitions, workspace/coach foods, coach-authored meal portions, and snapshots; external food databases are post-V1 and need their own provider/license register.
  • Manual macro entry is coach-only for now. Clients log structured foods, portions, hydration, supplements, notes, and completion states, but they do not submit free-form macro totals in Public V1.
  • Client mobile prefetches the active plan snapshot, referenced food facts, and Today tasks for today plus the next 7 local days; it keeps recent accepted history and sync receipts for review/retry without caching the full catalog.
  • Supplement nutrients count toward totals only when the coach marks the item as nutrition-bearing, such as protein powder or a meal replacement. Creatine, magnesium, vitamins, and minerals remain adherence-only in Public V1 unless explicitly re-scoped later.
  • Clients see friendly target bands and remaining/over hints only for client-visible targets. Coaches see numeric variance, source confidence, repeated-pattern flags, and coach-only severity scoring.
  • Clients can correct a submitted daily nutrition log until the coach reviews that day or 24 hours after submission, whichever happens first; later changes require coach/support correction or a clarification flow.

CoachMe internal planning documentation.