Intake tracking (CreatorTrack)¶
URL: https://mkdocs.justinsforge.com/memory/general/reference_intake_tracking/
The universal "what I put in my body" ledger: supplements (creatine), prescriptions (finasteride), OTC meds (acetaminophen), and food-like items (protein shakes). One ledger so any dose can be correlated against wellness (HRV, sleep) over the long term. Built in CreatorTrack (forge-suite), the live product. (The old workspace.justinsforge.com Flask surface was retired 2026-07-04; see lifeos spine.) Plan: intake-tracking-creatortrack-2026-07-03.
Naming¶
- Intake = the feature/ledger. Item = a catalog row (a thing you can take). Intake entry = one logged dose. (Chose "Item" over "substance", too clinical.)
Storage , dedicated typed tables (deliberate)¶
Meals/habits in forge-suite use the generic collection engine (database_row nodes +
collection_values jsonb). Intake does NOT: it uses dedicated typed tables queried
directly by forge-suite/lib/intake.ts via sql wrapped in asUser RLS. Chosen for
clean wellness-correlation joins, dose math, and adherence (jsonb cells make those messy).
Migration forge-suite/db/migrations/20260703T230952_..._intake_dedicated_tables.sql
(schema authored in forge as the parked 0041_intake.sql; a table-level UNIQUE(lower(name))
is illegal in Postgres, so it is a CREATE UNIQUE INDEX that also serves as the upsert
ON CONFLICT arbiter). Star schema, all workspace-scoped via core.apply_workspace_rls:
app.intake_items, catalog:name,kind text[](facet: food|supplement|rx|otc, a CHECK-constrained subset, NOT a folder),default_dose+dose_unit,route,is_rx,schedule(scheduled|prn),rxnorm, optional macros (calories/protein_g/carbs_g/ fat_g/caffeine_mg).UNIQUE (workspace_id, lower(name))via index.app.intake_ingredients, multi-active products (a pre-workout = caffeine + beta-alanine), so "total caffeine today" is one query.app.intake_entries, the fact ledger, one row per dose:item_id,dose,dose_unit,route,reason,source,logged_at,logged_on.app.intake_regimens, scheduled items only, for adherence (expected vs actual).
Where it lives (2026-07-04 , not its own app)¶
Intake is a tab inside the Food Log (meals) app, NOT a standalone app. It is a rail
entry (id:"intake") in components/apps/meals/MealsClient.tsx rendering IntakeClient
in the meals main pane; the standalone app registration (SYSTEM_APPS card, registry
mapping, IntakeApp.tsx) was removed. Backend is unchanged: dedicated tables, capture verb,
read API, and the intake node (kept as the tables' FK anchor + a valid APP_NODE_TYPES).
How it relates to other surfaces¶
- Macros -> Food Log day totals (LIVE). Macro-bearing intake entries fold into the meals
day view calorie/macro totals (
DayView.tsx, only when the viewed day matches the fetched intake date; no double count since intake lives only inapp.intake_*). "· incl. intake" hint on the calorie card. A protein shake logged as intake now counts toward nutrition. - Scheduled -> auto-ticks a habit (LIVE). Logging a dose of a
schedule='scheduled'item alsocreateHabitDef(idempotent) +logHabit(idempotent per habit+day) a same-named habit, so you get an adherence streak in the Habits app. Failure logs loud but never blocks the dose write (dose is source of truth). PRN items get no habit. - Fitness-tagged (creatine) -> correlates vs HRV/training; a fitness panel is later.
- PRN (acetaminophen) -> logged only when taken, carries a
reason. - Wellness metrics are measurements, not intake; Intake correlates against them by timestamp.
Capture (log a dose)¶
Text any AI/Telegram bot; the Item auto-seeds on first mention. Capture verb intake
(forge-suite/app/api/capture/route.ts -> createIntakeEntry in lib/intake.ts).
Contract: POST /api/capture {verb:"intake", workspace, name, dose, unit, kind, route,
reason, isRx|is_rx, schedule, source}. Read: GET /api/apps/intake?workspaceId&date ->
{ok, ids, items, entries}.
Python client (scripts/forge_workspace_capture.py, the canonical bot door):
- log_intake(name, dose, unit, kind, route, reason, is_rx, schedule, brand, source, date, notes)
- intake_day(brand, date) -> {entries, items}
Rollup + correlation (scripts/forge_intake_rollup.py, read-only)¶
Uses forge_workspace_db.admin_connect() (owner, bypasses RLS; filters workspace_id).
- intake_day_rollup(date) -> day's entries + macro subtotal.
- intake_vs_wellness(metric, days) -> per-day intake count paired with a
wellness.wellness_facts metric (hrv/sleep/...) for correlation.
- CLI: python3 forge_intake_rollup.py --date YYYY-MM-DD | --vs hrv --days 14.
The Intake tab surface (components/apps/intake/IntakeClient.tsx)¶
A full daily surface inside the Food Log (pill icon in the meals rail), top to bottom:
1. Header , date chooser (shared DatePicker) + Sunday-start weekday progress rings
(fraction = scheduled items taken that day / total scheduled).
2. Checklist , scheduled items as circular check rows; check = log today's dose
(auto-ticks the same-named habit), uncheck = delete today's entry (+ clears the habit).
"Add a daily supplement" creates a scheduled Item. Interactive for TODAY only; past days
review-only (a dose stamps logged_on = today).
3. Taken , read-only list of every dose for the selected day (incl. prn + past days).
4. Adherence stats , per-item current/best streak, completions, adherence %, 30/90d bar
chart (components/apps/intake/intakeMath.ts).
5. History , weekly accordion (Sunday-start, newest first, expand a week -> its days +
doses; shared EntryRow with Taken). Draws from the single ~90d fetch (no load-older yet).
Write ops (app/api/apps/intake/route.ts POST, mirror habits/meals): check {itemId,date},
uncheck {itemId,date}, add_item {name,schedule?,kind?,dose?,unit?}; GET accepts since
for the windowed read. lib/intake.ts: createIntakeEntry, ensureIntakeItem,
deleteTodayIntake (inverse-clears the auto-ticked habit), windowed getIntakeData.
Multi-step habits + supplement linking (2026-07-04)¶
A reusable multi-step habit primitive: a habit definition carries an ordered Steps
JSON array [{id,label,intakeItemId?,dose?,unit?}] (new "Steps" prop on habits.defs);
per-day completion is a Steps Done id array on the log row, with the def Goal Value =
step count so "done" + the progress ring reuse the existing valued-log logic. Plan:
multi-step-habits-intake-2026-07-04.
- One sync point:
toggleStep(defId, stepId, date, checked, dose?)(lib/habits.ts) updates the (habit,date) log's Steps Done + Value, and if the step links an Intake item, writes/removes that Intake dose (rolls the log back on intake failure; no half-updates). Both the habit step popup and the Intake checklist call it, so checking in either place syncs the other (via the shared ledger) and flows into Taken/History + the habit->task bridge. Linked items skip the old name-based auto-tick (no duplicate habit). - Link:
app.intake_items.habit_def_id/habit_step_idcolumns;linkItemToHabit/unlinkItemFromHabit(lib/intake.ts), exposed as intake POST opslink_habit/unlink_habit. - Habits POST ops:
set_steps/add_step/remove_step/reorder_step/toggle_step(session-auth). - UI:
components/apps/habits/StepPopup.tsx(check or dosage per step) +StepEditor.tsx(add/remove/reorder, link an Intake item); Intake tab per-item "add to habit" (HabitLinkControl) + linked-item checklist toggle routes throughtoggle_step. - Steps are GENERIC (any habit); a step with
intakeItemIdis the supplement case.
Status (2026-07-04)¶
Round-one backend deployed + live-verified on prod :3070. Rounds two + three (Food Log tab,
macro union, habit auto-tick, full daily surface: rings/checklist/Taken/stats/history)
plus multi-step habits (steps schema, toggleStep sync, popup/editor, intake link) committed
on forge-suite branch ct-notes-body-datetime-cells (through commit 8f9a525), all tsc
clean. Intake check/uncheck/add_item + windowed read HTTP-verified on dev :3060;
toggleStep (habit log + intake dose, both directions, no duplicate habit) DB-verified via
the real lib path. Multi-step UI compiles (habits ops are session-authed, so click-through
needs a logged-in browser session to eyeball). NOT on prod yet: :3070 is a frozen
next start build; redeploy from this branch to push everything to prod.
Ports: :3070 = app.creatortrack.ai (prod, next start); :3060 = dev.creatortrack.ai (next dev).
Known gaps / next¶
- First-mention-wins schedule: an Item's
schedule(prn|scheduled) is fixed at auto-seed and not updated by later logs, so the habit auto-tick only fires for items first seeded asscheduled. Bot NL parsing does not yet infer rx/scheduled (e.g. finasteride -> rx -> scheduled). Needs the edit UI or smarter capture. (Items already seeded prn, e.g. the test Creatine, won't tick until re-seeded/edited.) log_intake(date=...)backdating is a silent no-op (verb ignoresdate); adddateto the verb +createIntakeEntry.- Delete/trash UX (parked, round three): each entry gets a
...menu (Restore / Delete forever), same actions on right-click context menu, and select-all + right-click for bulk. - Later: fitness-supplements panel, correlation-vs-HRV charts, RxNorm auto-lookup, item edit UI.