[2026-01-06] Advanced Nutrient Tracking & Prompt Optimization
Task Metadata
Date: 2026-01-06
Status: Completed
Objective
Goal:
- Flexible Nutrient Tracking: Extend the data model to support arbitrary, user-defined nutrients (e.g., Fiber, Sodium) beyond the core macros.
- Prompt Optimization: Simplify the AI system prompt to be more efficient and model-agnostic, dynamically injecting the user's specific nutrient tracking list.
- Trigger: User request to track Fiber/Sodium and improve prompt compatibility.
- Reference: ADR 001: Flexible Nutrient Schema
- Constraints:
- Resource Efficiency: Minimize DynamoDB
GetItemrequests by using Frontend-Assisted Validation (passing snapshots to resolvers). - Backward Compatibility: Core macros must remain top-level.
- Data Integrity: Unit Immutability (units cannot be changed once a nutrient is created).
- Concurrency: Optimistic Locking on settings to prevent data loss on the "God Row".
- Resource Efficiency: Minimize DynamoDB
Technical Strategy
We will use Metadata-Driven Maps where CONFIG#TARGETS acts as the schema registry and the frontend provides validation snapshots to save on RCU.
- Registry: Nutrient definitions (key, unit, label) are permanent once created.
- Snapshotting: Frontend passes the current "Active Key List" to
addMealto validate AI response without a DB lookup. - Aggregation: Resolver rounds to 2 decimal places to prevent float drift.
- Versioning: Every settings update checks the
versionattribute to handle concurrent edits.
- Key Decisions:
- Combined Settings: Single row for targets + registry + history.
- Frontend Validation: Trust-but-verify approach to save cost while blocking reserved keywords.
- Report-Only: Accepting that extended nutrients won't be searchable via GSI.
Testing Strategy
- Must Test: Optimistic locking failure handling in Frontend.
- Must Test: Unit conversion edge case (ensuring users can't change units).
- Must Test: "The Math Ghost" regression: Log -> Disable -> Delete.
Risk Analysis
- Conflict Resolution: Users on two devices might see "Settings Update Failed" if they edit simultaneously.
- Math Drift: Minor precision loss over years (mitigated by rounding).
- Files to Modify:
graphql/schema.graphql(UpdateUserTargets, addregistrySnapshotinput)appsync/addMeal.js(Implement snapshot-based filtering)frontend/src/hooks/useSettings.ts(Implement optimistic locking logic)docs/architecture/data_model.md(AddnutrientRegistry,extendedNutrientsmap, andversionattribute)docs/architecture/ui_data_validation.md(Define Unit Immutability, Snapshot Protocol, and Version Conflict rules)docs/architecture/ux_definitions.md(Define dynamic summary scaling and Registry/Archival display logic)docs/architecture/testing_strategy.md(Add 'Concurrency & Immutability' to the Must-Test criteria)docs/architecture/decisions/001_flexible_nutrient_schema.md(Finalize as Technical Specification)
Execution Plan
- Step 1: Schema & Backend Foundation
- Update
docs/architecture/data_model.mdwith new attributes. - Add
versiontoUserTargets. - Implement
registrySnapshotparameter inaddMeal. - Update
appsync/addMeal.jswith rounding and keyword filtering.
- Update
- Step 2: Settings & Optimistic Locking
- Update
docs/architecture/ui_data_validation.mdwith locking/validation rules. - Update
updateUserTargetsresolver to check version. - Update Settings UI to handle "Locked Units" and conflict errors.
- Update
- Step 3: Prompt Engineering
- Dynamic prompt generator that injects
label (unit)for each active nutrient.
- Dynamic prompt generator that injects
- Step 4: Dashboard & UI Polish
- Update
docs/architecture/ux_definitions.mdfor dynamic columns. - Dynamic column rendering and summary display.
- Update
- Step 5: Verification & Testing
- Update
docs/architecture/testing_strategy.mdwith new test scenarios. - Implement Unit Tests:
promptUtils.ts(Verify dynamic prompt generation).mealParser.ts(Verify parsing of extended nutrients).addMeal.js(Verify resolver logic for filtering/rounding).
- Implement Integration Tests:
useMeals(Verify optimistic updates for extended nutrients).
- New Build Artifact: Created
scripts/evaluate_appsync.shandtask test:appsync:evalto catch runtime syntax errors locally. - Finalize
docs/architecture/decisions/001_flexible_nutrient_schema.md. - Run full test suite.
- Update
Execution Notes
- 2026-01-08: Implemented backend schema changes, resolvers (
addMeal,updateUserTargets), and frontend infrastructure (types,ApiMealService,useMeals,useSettings). - 2026-01-08: Implemented
promptUtils.tsto dynamically generate the AI prompt based on the user's registry. - 2026-01-10: Incident: UI Flashing/Reverting on Settings Update.
- Problem: When adding a new macro, the UI would show it momentarily then revert (flash).
- Discovery: Layer 3 (Hook) tests revealed two issues:
useSettings.tsmutation logic was not performing a deep merge onnutrientRegistry, causing optimistic updates to overwrite the entire registry with partial data.onSettledwas triggering an immediateinvalidateQueries, which caused a refetch that often returned stale data before the mutation's write had fully propagated in the backend (or before the cache was updated with the mutation's result).
- Fix:
- Refactored
onMutateto perform a deep merge of the registry. - Updated
onSettledto manually update the cache with the actual response from the server before invalidating, ensuring the "latest" data is always present. - Hardened the
updateUserTargets.jsresolver response mapping to be more consistent.
- Refactored
- Verification: Created
frontend/src/hooks/__tests__/useSettings.test.tsxto explicitly test optimistic state retention. All tests passed.
- 2026-01-10: Incident: Nutrient Registry lost on Persistence.
- Problem: Even when mutations succeeded, the
nutrientRegistrywould return asnullin the UI and staynullafter refresh. - Discovery: Analysis of CloudWatch logs revealed two critical oversights in
updateUserTargets.js:- Double-Parse Bug:
AWSJSONinputs are automatically parsed by AppSync. Re-parsing withJSON.parse()on an object caused the value to be corrupted/cleared. - Missing Casing Fallback: The resolver lacked fallbacks for legacy
PascalCaseattributes, causing null returns if the DB state was not standardized.
- Double-Parse Bug:
- Fix:
- Implemented a "Safe Parse" pattern in
updateUserTargets.jsandaddMeal.js(only parse if type is string). - Added robust casing fallbacks to both Query and Mutation resolvers.
- Note on
returnValues: Attempted to addreturnValues: 'ALL_NEW', but discovered it is unsupported in theAPPSYNC_JS1.0.0 runtime (causes a Code error). Relied on safe parsing and casing fallbacks instead. - Updated
updateUserTargets.test.jsto catch these regressions.
- Implemented a "Safe Parse" pattern in
- Problem: Even when mutations succeeded, the
- 2026-01-10: Incident: UI not rendering Registry despite successful Parse.
- Problem: Even after fixing the backend, the UI still didn't show the nutrients.
- Discovery:
ApiMealService.tswas attempting to parse thenutrientRegistryin-place on the GraphQL response object. Some clients provide immutable or proxy-wrapped objects where in-place modification is lost or ignored by TanStack Query's cache. - Fix: Updated
ApiMealService.tsto return a new object with the parsed registry, ensuring the cache receives a fresh, correctly-typed object.
- 2026-01-10: Incident: String-Hell Rehydration.
- Problem: Nutrient registry kept reverting to a JSON string on refresh, breaking the UI.
- Discovery: The persistent cache (localStorage) was re-hydrating stale string data from previous sessions before the API fetch could run.
- Fix: Implemented a "Final Defense" using React Query's
selectoption inuseSettings.ts. This ensures data is definitively transformed into an object every time it is accessed from the cache.
- 2026-01-10: Feature: Historical Nutrient Visibility.
- Updated
DailyTotalsto show nutrients if they are Active OR have a non-zero total for that day. - Archived nutrients with data are now styled with 60% opacity and an "(Arc)" label, preserving historical context.
- Updated
- 2026-01-10: Feature: Nutrient Suggestions & History Restore.
- Added "Quick Add" buttons for common nutrients (Fiber, Sodium, Sugar, Sat. Fat) to the settings UI.
- Suggestions automatically filter out items already in the registry.
- Added a "History" section to settings for one-tap restoration of archived nutrients with their original units.
- 2026-01-10: Advanced Nutrient Polish & Mobile Optimization.
- Compact UI: Redesigned
DailyTotalsto move labels and numeric data inside the progress bars, significantly reducing vertical height. - Readability (Dual-Layer Text): Implemented a sophisticated CSS clipping strategy in
ProgressBar.tsxto render two layers of text (zinc background / white foreground). Text "color-shifts" dynamically as the bar fills, ensuring perfect contrast. - Tighter Spacing: Optimized spacing (
space-y-2) and standardized bar heights (h-6macros,h-5extended) for a denser, more professional mobile dashboard. - Layout Simplification: Defaulted to the Vertical Layout as the single source of truth; removed redundant horizontal layout code and preference hooks.
- Visual Polish:
- Rounded logged meal calories to whole numbers to remove floating point artifacts.
- Unified "Macro Pills" styling across
MealItemandPendingMealCard. - Implemented dynamic color palette: Protein (Rose), Fiber (Emerald), Sodium (Sky), Sugar (Pink).
- Compact UI: Redesigned
- 2026-01-10: Stability & Task Optimization.
- iOS Fix: Refactored date/time picker to use a transparent overlay, resolving issues where iPhone would block the system picker.
- Workflow Speed: Optimized
Taskfile.ymlwithsourcesdirectives, enabling instant skipping of up-to-date tests, linting, and type-checking. - Loading Shield: Added a "Loading Shield" to
Dashboard.tsxto preventTypeErrors during initial load. - Verification: Final test count: 88 passing (31 backend, 57 frontend). Includes 4 new component/hook integration tests.
- 2026-01-10: Incident: Persistent AppSync JS Deployment Errors.
- Problem: Deployment repeatedly failed with "The code contains one or more errors," even when logic passed local Vitest and official linter.
- Discovery:
APPSYNC_JSruntime has silent bans not caught by standard linting (e.g., passing functions as arguments inside.forEach()is forbidden). - Surgical Fix: Identified via
aws appsync evaluate-codethatutil.toJson()andutil.parseJson()are invalid inAPPSYNC_JS1.0.0. Switched to nativeJSON.stringify()andJSON.parse(). - Success Protocol: Replaced all closures with
for...ofloops and moved to strictlet/constwith incremental object construction. - Final Result: Backend successfully deployed to TEST environment via
task deploy:test. Total test count: 66 passing (at time of incident).
User Approval & Key Learnings
Key Learnings
- Performance vs Security: Decided to pass a "Registry Snapshot" from the frontend to valid AI data instead of a "Read-Before-Write" DB hit, prioritizing cost efficiency.
- Unit Integrity: Identified that changing units on a live tracking key would corrupt aggregate history; enforced Unit Immutability.
- Runtime Realities: The
APPSYNC_JSruntime validator is more primitive than the engine itself. Native JS globals (JSON,Math) are more reliable thanutilhelpers for standard tasks. - The Persistence Trap: Found that
PersistQueryClientProvidercan re-hydrate stale JSON strings fromlocalStorageinto an otherwise type-safe application. Solved using React Queryselectas a definitive transformation layer. - Atomic Realities: Discovered that updating nested maps in DynamoDB (
SET map.key) fails if the map doesn't exist yet. Switched to flat atomic counters withif_not_existsfor 100% reliability on new days. - UX Density: Moving text inside bars proved to be the superior mobile strategy, allowing 5+ metrics to fit comfortably above the fold.
Final Approval: Feature complete and hardened. (User to mark as Complete)
Context Memory (AI-Only)
Summary for Future Context
The Flexible Nutrient Schema is implemented using a Metadata-Driven Map in DynamoDB. The CONFIG#TARGETS item acts as the registry for user-defined nutrients (Fiber, Sodium, etc.). Frontend provides a registrySnapshot to mutations to avoid extra DB lookups.
Critical Development Rule: When writing APPSYNC_JS resolvers, avoid util.toJson/parseJson (use native JSON instead) and avoid .forEach() or any function-passing (use for...of). Always verify new resolvers using aws appsync evaluate-code.