Skip to content

2026-01-03: Data Format Uplift

Task Metadata

  • 📆 Date: 2026-01-03
  • 🚥 Status: Complete

1. Context

  • Goal: Standardize data formats across the stack (Casing & Types) to resolve tech debt and enable decimal precision.
  • Trigger: "Layered Testing" task identified mixed casing (Pascal vs Camel) and artificial integer constraints blocking decimal inputs.
  • Scope:
    1. Casing Unification: Standardize on camelCase for API/Frontend interactions.
    2. Float Support: Update GraphQL Schema and Frontend to support Float types for Calories and Macros.
    3. Prompt Engineering: Update LLM instructions to output floats.

2. Approach

  • Technical Strategy:

    • Schema: Update graphql/schema.graphql (Change Int to Float).
    • Frontend: Update SettingsModal and useMeals to remove Math.round() casts.
    • Backend (AppSync): Ensure resolvers handle floats correctly (DynamoDB N type supports this natively).
    • Data Migration:
      • Write a Python script (scripts/migrate_data_format.py) to scan DynamoDB.
      • Standardize keys to camelCase if necessary (or confirm AppSync mapping handles this).
      • Decision Required: Do we migrate existing Integer data to Floats? (DynamoDB doesn't distinguish 1 from 1.0, but we should verify).
  • Key Decisions:

    • Float Precision: Use standard floating point. UI display logic will handle rounding for display (e.g. 1 decimal place).
    • Casing: Ensure frontend sees camelCase but DynamoDB stores consistent keys (likely PascalCase if that's the current STD pattern, or switch entire stack to camelCase). Investigation needed.
  • Testing Strategy:

    • Must Test:
      • Decimal Inputs: Verify 1.5 protein is saved and retrieved as 1.5.
      • Legacy Data: Verify old Integer records still load correctly.
      • Casing: Verify frontend does not break if backend returns PascalCase keys (or fix the mapping).
    • Skip:
      • Complex historical data migration if the schema change is backward compatible.

3. Impact Analysis

  • Files to Modify:
    • graphql/schema.graphql
    • frontend/src/components/SettingsModal.jsx
    • frontend/src/utils/constants.js (Prompt templates)
    • appsync/addMeal.js (Resolver logic)
  • Risks:
    • Breaking Change: Changing GraphQL types from Int to Float might break strict clients, though JS/JSON is usually forgiving.
    • Data mismatch: If Frontend expects camelCase but DB returns PascalCase (without AppSync mapping), UI will show empty data.

4. Execution Plan

  • Step 1: Investigation
    • Audit current DynamoDB casing: PascalCase (Calories, MealSummary) used in DB and Meal type.
    • AppSync Mapping: UserTargets manually maps Pascal to camelCase. Meal does not (direct pass-through).
    • Type Constraint: Enforced strictly by Int in schema.graphql. Resolvers are type-agnostic.
  • Step 2: GraphQL Schema Update
    • Updated schema.graphql to use Float and camelCase.
    • Standardized addMeal mutation arguments.
  • Step 3: Frontend Update
    • Updated ApiMealService.js to use the new camelCase fields and float types.
    • Updated useMeals.js to support both legacy and new casings for totals calculation and optimistic updates.
    • Updated SettingsModal.jsx to support decimal targets and increased the input step to 5g for macros.
    • Updated Dashboard.jsx and PendingMealCard.jsx to be casing-resilient during the migration.
  • Step 4: Prompt Engineering
    • Updated SYSTEM_PROMPT in constants.js to output camelCase and decimals.
  • Step 5: Verification
    • Added getMeals.test.js to verify backend mapping layer for mixed casings.
    • Added MealItem.test.jsx to verify frontend rendering resilience.
    • Added mealParser.test.js to verify JSON normalization logic for all casing formats.
    • Run full test suite (30 passing).
  • Step 6: Documentation
    • Updated docs/architecture/data_model.md to reflect camelCase and Float standards.
    • Updated docs/architecture/ui_data_validation.md to reflect Float support and new macro steps.

5. Execution Notes

  • Audit Findings:
    • DynamoDB attributes are primarily PascalCase.
    • GraphQL Meal type is PascalCase, but UserTargets is camelCase.
    • Mutations use a mix of snake_case (addMeal) and camelCase (updateUserTargets).
    • Uplift Strategy: We will standardize the entire API layer on camelCase and Float. We will implement a "Mapping Layer" in AppSync to handle legacy PascalCase data in DynamoDB to avoid an immediate mass migration of production data.
  • Implementation Notes:
    • Architecture: Extracted JSON parsing logic into utils/mealParser.js. This centralizes the "Casing Mapping" logic, making the UI components (Dashboard, PendingMealCard) agnostic to the source data format.
    • Casing Resilience Logic:
      • Backend (API Return): The getMeals.js and getUserTargets.js resolvers now check both casings when mapping DynamoDB attributes to GraphQL fields (e.g., item.calories || item.Calories). This ensures that the Frontend always receives consistent camelCase regardless of the storage state.
      • Frontend (Ingestion): The mealParser.js utility uses nullish coalescing to support three input formats: camelCase (New), snake_case (Legacy LLM), and PascalCase (Direct DB copy).
      • Frontend (UI): Components like MealItem.jsx and PendingMealCard.jsx are now agnostic to the raw object casing, relying on the normalization performed by the Parser and API mapping layer.
    • Restored technical comments in useMeals.js that were accidentally removed during the casing refactor.
    • Adjusted SettingsModal input steps: Macros (Protein/Fat/Carbs) now step by 5g for better usability, while Energy remains at 1 (kcal).
    • Verified that PendingMealCard and MealItem render floats with .toFixed(1) for precision without UI clutter.
  • Hotfix (2026-01-04): Replaced nullish coalescing operator (??) in deleteMeal_step2.js with explicit undefined checks to ensure compatibility with the APPSYNC_JS runtime, which resolved a deployment failure in the Test stack.
  • Safety Warning: Since the project currently uses a single DynamoDB table (MinimalCalorieMeals) for both Test and Prod stacks, the data migration script MUST NOT be run until the new code (which is casing-resilient) is fully deployed to Production.

6. User Approval & Key Learnings

  • User Approval: Approved on 2026-01-03.
  • Key Learnings:
    • DynamoDB Data Types: A key discovery was that DynamoDB's Number type already natively supports both integers and decimals. The primary restriction was in the GraphQL Schema (Int vs Float), which necessitated the uplift to enable decimal precision in the application.
    • Centralized Normalization: Extracting normalization logic into utilities (mealParser.js) significantly reduces UI component complexity and ensures consistent data handling across various input methods (links, clipboard, manual logging).
    • Casing Resilience: Implementing a "Double-Check" pattern in AppSync and the Parser allows for a zero-downtime transition between different naming standards.

7. Context Memory (AI-Only)

Summary for Future Context

Successfully migrated the data format standard from PascalCase/Integers to camelCase/Floats.

Key Changes:

  • GraphQL: All numeric fields are now Float. All field names are camelCase.
  • AppSync: Resolvers getMeals, getUserTargets, and updateUserTargets now handle both legacy and new casings. addMeal now writes new items in camelCase (version 4).
  • Frontend: mealParser.js handles ingestion of all formats. SettingsModal and MealItem support decimals.
  • Migration: scripts/migrate_data_format.py is ready but should only be run after full production deployment.

Next Task: Deployment to dev/test environment to verify these changes in a live environment before merging to production.