Asher’s Progress Tracker

Asher’s progress tracker

How the tracker stores his data

The app records, it does not judge. Every intervention, whether prescription, supplement, homeopathic, dietary or therapeutic, is stored in the same table with the same fields. Nothing is ranked or given an evidence score. Analysis comes only from what is logged.

Tables
33
Derived views
24
Functions
9
Database
Postgres on Supabase

Measures

measures7 fieldsWhat each logged measure means, so views never guess which end of a scale is good. Shared reference data, not per child.
FieldTypeDetails
keytextkey
labeltextrequired
sourcetextrequired daily_scoredaily_countdaily_skillweekly
daily_skill: 0–10 skill ratings shown under Skills on Today
directiontextrequired lower_betterhigher_betterneutral
scale_minnumber
scale_maxnumber
sort_orderwhole number

Core

children11 fields
FieldTypeDetails
ididkey
user_ididrequired default signed-in user
nametextrequired
dobdaterequired
timezonetextrequired default 'Europe/London'
Day boundary for meals and logs. Rolling loads and "which day was this meal" are computed in this zone.
illness_tail_dayswhole numberrequired default 14
Analysis settings. Kept here rather than hardcoded so they can be retuned and every derived view follows, including for past data.
adherence_thresholdnumberrequired default 0.8
observed_min_nwhole numberrequired default 8
days eaten AND not eaten
observed_lag_dayswhole numberrequired default 1
0 = same day, 1 = next day
observed_points_per_scorenumberrequired default 1.0
How many points of OCD difference (eaten vs not) equal one point on the 0–3 histamine scale, so observed scores sit on the seed’s scale.
created_atdate and timedefault now

Linked from interventions.child_id, protocol_entries.child_id, protocol_documents.child_id, daily_logs.child_id, changes.child_id, weekly_ratings.child_id, foods.child_id, meals.child_id, saved_meals.child_id, thresholds.child_id, food_trials.child_id, illness_episodes.child_id, illness_protocol_steps.child_id, observations.child_id, patterns.child_id, media.child_id, progress_events.child_id, library_items.child_id, atec_scores.child_id, growth.child_id, test_results.child_id, monthly_reviews.child_id

interventions7 fieldsEvery substance, therapy or dietary rule that can be "on" or "off".
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
nametextrequired
"Fish oil", "Rifaximin", "Ferrum phos"
categorytextsupplementmedicationhomeopathicessential_oildiettherapydevice
unittext
ml, mg, drops, capsules, sessions
notestext
archivedyes / norequired default false

Linked from protocol_entries.intervention_id, changes.intervention_id, illness_protocol_steps.intervention_id, dose_events.intervention_id, library_interventions.intervention_id

Protocol state

protocol_entries9 fieldsThe active protocol on any date is every row where started_on <= date and (ended_on is null or ended_on >= date). Nothing is re-entered daily.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
intervention_ididrequired links to interventions
dosenumber
frequencytext
"2x daily", "nightly", "as needed"
routetextoraltopicalinhaledrectal
started_ondaterequired
ended_ondate
null = currently active
notestext
Table rules
  • check (ended_on is null or ended_on >= started_on)
  • exclude using gist (intervention_id with =, daterange(started_on, ended_on, '[]') with &&)

Linked from daily_protocol_log.protocol_entry_id, changes.ended_entry_id, changes.started_entry_id

protocol_documents8 fieldsThe protocol as issued by the practitioner, stored alongside the structured version so the two can be reconciled. AI extraction proposes protocol_entries; nothing is written until the user confirms each row.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
issued_ondate
sourcetext
practitioner or origin
storage_pathtextrequired
PDF, photo or scan
notestext
reconciledyes / norequired default false
created_atdate and timedefault now

Daily log

daily_logs40 fieldsOne row per day. Everything optional except log_date, so a partial day is still saved. Moved ahead of adherence, which references it.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
log_datedaterequired
agitationwhole numberrange 1–10
DAILY SCORES, 1-10. Direction lives in the measures table so 1 and 10 never swap meaning, and views can flip difficulty measures.
anxietywhole numberrange 1–10
ocdwhole numberrange 1–10
stresswhole numberrange 1–10
hyperactivitywhole numberrange 1–10
face_tensionwhole numberrange 1–10
moodwhole numberrange 1–10
behaviourwhole numberrange 1–10
speechwhole numberrange 1–10
sugar_intakewhole numberrange 0–3
Tracked independently of histamine load, not folded into it.
sugar_notestext
what it was: "biscuit, half a juice"
sleep_hoursnumber
OBJECTIVE COUNTS AND DURATIONS. Facts, not judgements; never converted to a score. Sleep fields describe the night ending on the morning of log_date. Poo count is derived from stool_events, not stored.
night_wakingswhole numberdefault 0
night_awake_hoursnumber
wee_accidentswhole numberdefault 0
wee_accident_gradeslist of textrequired default '{}' lightheavyvery_heavy
One grade per accident; wee_accidents is their count. Days logged before grading existed have a count and no grades.
meltdownswhole numberdefault 0
upside_down_countwhole numberdefault 0
times he goes upside down
wrist_to_bodywhole numberdefault 0
SELF-DIRECTED AND OUTWARD-DIRECTED BEHAVIOURS, counted separately. Self-injury and aggression often move independently; a blended count hides which changed. hitting own body with wrist
wrist_to_headwhole numberdefault 0
hitting own head with wrist
head_to_floorwhole numberdefault 0
head down/hanging on floor
head_butt_personwhole numberdefault 0
head banging into a person
scratching_personwhole numberdefault 0
behaviour_notestext
spontaneous_wordswhole number
table_sitting_minswhole number
longest sit to eat
lesson_sitting_minswhole number
longest sit for a lesson
playwhole numberrange 0–10
Skills, 0–10 (0 = not at all, 10 = excellent)
pretend_playwhole numberrange 0–10
park_playwhole numberrange 0–10
interactionwhole numberrange 0–10
daily_speech_claritywhole numberrange 0–10
neurotypicalwhole numberrange 0–10
confounderslist of textrequired default '{}'
Things that could explain a bad day other than food or protocol: fever, nappy_rash, travel, teething, poor_sleep, visitors… The app offers a fixed list so the same thing is always spelled the same way.
food_notestext
day_notestext
created_atdate and timedefault now
Table rules
  • unique (child_id, log_date)
  • check (sleep_hours >= 0 and night_wakings >= 0 and night_awake_hours >= 0 and wee_accidents >= 0 and meltdowns >= 0 and upside_down_count >= 0 and wrist_to_body >= 0 and wrist_to_head >= 0 and head_to_floor >= 0 and head_butt_person >= 0 and scratching_person >= 0 and spontaneous_words >= 0 and table_sitting_mins >= 0 and lesson_sitting_mins >= 0)

Linked from daily_protocol_log.daily_log_id, meals.daily_log_id, stool_events.daily_log_id, media.daily_log_id

daily_protocol_log6 fieldsWhat he was prescribed and what he actually received are different facts. Only deviations are stored; a day with a daily_logs row and no entry here means every active item was given as prescribed. A day with no daily_logs row is unknown, not adherent.
FieldTypeDetails
ididkey
daily_log_ididrequired links to daily_logs (deleted with it)
protocol_entry_ididrequired links to protocol_entries
statustextrequired default 'as_prescribed' as_prescribedpartialmisseddose_changedrefused
actual_dosenumber
only if it differed
notestext
Table rules
  • unique (daily_log_id, protocol_entry_id)

Single-variable change windows

changes15 fieldsA change opens a window of window_days days starting on changed_on (window_end is the first day after it). A second change whose window overlaps an existing one is refused unless the user overrides, which sets overlapped = true. Enforced by trigger in 20261025000300_functions.sql.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
intervention_ididrequired links to interventions
ended_entry_ididlinks to protocol_entries
The protocol rows this change ended and started, so changes and protocol_entries cannot drift apart. Written together by record_change().
started_entry_ididlinks to protocol_entries
change_typetextrequired addedremovedincreaseddecreased
from_dosenumber
to_dosenumber
changed_ondaterequired
window_dayswhole numberrequired default 5 check window_days > 0
window_enddatecomputed
verdicttextrequired default 'pending' toleratednot_toleratedunclearpending
verdict_notestext
overlappedyes / norequired default false
closed_atdate and time

Linked from event_triggers.change_id

weekly_ratings11 fieldsCapacities that change over weeks, not days.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
week_startdaterequired must be a Monday
Monday
posturewhole numberrange 1–10
1-10, capacity measures: 1 = poor, 10 = excellent
strengthwhole numberrange 1–10
motor_skillswhole numberrange 1–10
cognitionwhole numberrange 1–10
speech_claritywhole numberrange 1–10
understandingwhole numberrange 1–10
notestext
submitted_atdate and time
Table rules
  • unique (child_id, week_start)

Food and meals

foods16 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
nametextrequired
food_grouptextproteinvegetablefruitcarbohydratefatother
flaggedyes / norequired default false
on the known-reaction list
statustextrequired default 'allowed' allowedexcludedtrial_queuedin_trialnever_tried
excluded_ondate
excluded_reasontext
exclusion_confoundedyes / no
Exclusion decided during illness or stacked changes: a weak verdict.
histamine_seedwhole numberrange 0–3
Seed value from published lists. Deliberately rough; superseded by observed_score once the user approves a proposal.
perishableyes / norequired default false
Storage time meaningfully raises histamine: fish, meat, cooked protein.
oxalate_seedwhole numberrange 0–3
sugar_seedwhole numberrange 0–3
observed_scorenumberrange 0–3
From this child’s own data, on the same 0–3 scale as the seed. Proposed by v_observed_scores, written only after user approval.
observed_nwhole numberrequired default 0
notestext

Linked from meal_items.food_id, saved_meal_items.food_id, food_trials.food_id, event_triggers.food_id

meals10 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
daily_log_ididlinks to daily_logs (deleted with it)
eaten_atdate and timerequired
meal_typetextbreakfastlunchdinnersnackother
photo_pathtext
private bucket
locationtexthomeschoolother_houseout
notestext
ai_extractedyes / norequired default false
user_confirmedyes / norequired default false
must be true before scoring

Linked from meal_items.meal_id

meal_items6 fields
FieldTypeDetails
ididkey
meal_ididrequired links to meals (deleted with it)
food_ididrequired links to foods
portiontextrequired default 'normal' tracesmallnormallarge
freshnesstextrequired default 'fresh' freshleftover_24hleftover_over_24hfrozen_then_reheated
Applies to perishable foods only; histamine accumulates in the fridge.
notestext

Linked from event_triggers.meal_item_id

saved_meals7 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
nametextrequired
"chicken, rice and pear"
photo_pathtext
notestext
times_usedwhole numberrequired default 0
last_used_ondate

Linked from saved_meal_items.saved_meal_id

saved_meal_items5 fields
FieldTypeDetails
ididkey
saved_meal_ididrequired links to saved_meals (deleted with it)
food_ididrequired links to foods
portiontextrequired default 'normal' tracesmallnormallarge
freshnesstextrequired default 'fresh' freshleftover_24hleftover_over_24hfrozen_then_reheated
thresholds9 fieldsUser-editable, so flags reflect his current tolerance.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
measuretextrequired histaminesugaroxalate
amber_atnumberrequired
red_atnumberrequired
single_item_rednumber
any single item at or above this
rolling_48h_ambernumber
Today + yesterday. Kept separate from the daily thresholds because a two-day sum is not comparable to a one-day limit. The amber level gates food trials.
rolling_48h_rednumber
updated_atdate and timedefault now
Table rules
  • unique (child_id, measure)
  • check (red_at >= amber_at)
  • check (rolling_48h_red >= rolling_48h_amber)
food_trials15 fieldsStructurally the same as a protocol change: one variable, a clean window, a verdict. clean_start and gate_notes are set by trigger at insert, from the gate conditions, not trusted from the client.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
food_ididrequired links to foods
trial_typetextrequired retestnew_food
started_ondaterequired
portiontextrequired default 'trace' tracesmallnormallarge
freshnesstextrequired default 'fresh' freshleftover_24hleftover_over_24hfrozen_then_reheated
clean_startyes / no
gate_notestext
window_dayswhole numberrequired default 3 check window_days > 0
window_enddatecomputed
verdicttextrequired default 'pending' toleratednot_toleratedunclearpending
verdict_notestext
next_portiontexttracesmallnormallarge
A tolerated trace does not clear normal portions; escalation is a separate trial.
closed_atdate and time
stool_events6 fieldsCount alone is not informative; type is. Daily poo count is derived here.
FieldTypeDetails
ididkey
daily_log_ididrequired links to daily_logs (deleted with it)
occurred_atdate and time
stool_typetextrequired normalpelletylooseexplosive_greenhardmucus
accidentyes / norequired default false
notestext

Illness episodes

illness_episodes10 fieldsIllness days are not stored on daily_logs. v_illness_days derives them (episode plus children.illness_tail_days), so changing the tail updates history too.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
started_atdate and timerequired
"first sneeze" button
first_symptomtextdefault 'sneeze'
ended_ondate
null = open
peak_severitywhole numberrange 0–3
fever_maxnumber
recovery_notestext
baseline_regained_ondate
may lag ended_on
regained_fullyyes / no

Linked from dose_events.episode_id, media.episode_id, event_triggers.episode_id

illness_protocol_steps9 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
intervention_ididrequired links to interventions
scheduletextrequired
human-readable
interval_minswhole number
for timed prompting
max_doseswhole number
null = until episode closed
then_scheduletext
e.g. "3x daily after 6 doses"
start_offset_minswhole numberrequired default 0
sort_orderwhole number
dose_events7 fields
FieldTypeDetails
ididkey
episode_ididrequired links to illness_episodes (deleted with it)
intervention_ididrequired links to interventions
due_atdate and time
given_atdate and time
null and past due = missed
skippedyes / norequired default false
notestext

Observations

observations10 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
noticed_ondaterequired
titletextrequired
"one knee down, other on chair"
descriptiontext
tagslist of textrequired default '{}' posturestimmotorbehaviourphysicalspeechsocial
statustextrequired default 'ongoing' ongoingresolvedincreasedintermittent
resolved_ondate
follow_up_duedate
+3 months, re-asked at review
created_atdate and timedefault now

Linked from media.observation_id

patterns9 fieldsPatterns the parent has noticed ("fewer than 2 poos a day → more agitation"). Kept as her own words; check_kind links a pattern to a built-in comparison against logged data, shown with its n.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
noticed_ondaterequired default current_date
titletextrequired
descriptiontext
examplestext
dates and what happened
statustextrequired default 'watching' watchingseems_truenot_borne_out
check_kindtextpoo_countfull_moon_histamine
created_atdate and timedefault now

Media

media10 fieldsMoved ahead of progress_events, which references it.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
storage_pathtextrequired
private bucket
kindtextphotovideodocument
captured_atdate and time
captiontext
tagslist of textrequired default '{}'
daily_log_ididlinks to daily_logs (cleared if deleted)
observation_ididlinks to observations (cleared if deleted)
episode_ididlinks to illness_episodes (cleared if deleted)

Linked from progress_events.media_id

Gains and setbacks

progress_events15 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
event_typetextrequired gainsetbackongoing
ongoing = still there and not changing ("still the same")
occurred_ondaterequired
when it started
noticed_ondate
when it was logged, if later
date_approxyes / norequired default false
titletextrequired
"said ‘hi mama’ unprompted"
descriptiontext
domaintextrequired speechcognitionsocialmotorplayregulationbehaviourself_caresleepphysical
domain is the first of domains, kept for older plugin versions.
domainslist of textrequired default '{}' speechcognitionsocialmotorplayregulationbehaviourself_caresleepphysical
magnitudewhole numberrange 1–3
statustextrequired default 'current' currentrecoveredpartial
recovered_ondate
media_ididlinks to media (cleared if deleted)
created_atdate and timedefault now

Linked from event_triggers.event_id

event_triggers11 fieldsA suspected cause is a link to the actual record, not typed text, so it can be counted. Nothing is attributed automatically.
FieldTypeDetails
ididkey
event_ididrequired links to progress_events (deleted with it)
trigger_kindtextrequired illnessfoodchangeexposureconfounderunknown
episode_ididlinks to illness_episodes
meal_item_ididlinks to meal_items
change_ididlinks to changes
food_ididlinks to foods
confoundertext
fever, nappy_rash, travel…
sourcetextrequired default 'user' usersuggested_accepted
confidencewhole numberrange 1–3
notestext
Table rules
  • check (case trigger_kind when 'illness' then episode_id is not null and num_nonnulls(meal_item_id, change_id, food_id, confounder) = 0 when 'food' then num_nonnulls(meal_item_id, food_id) = 1 and num_nonnulls(episode_id, change_id, confounder) = 0 when 'change' then change_id is not null and num_nonnulls(episode_id, meal_item_id, food_id, confounder) = 0 when 'confounder' then confounder is not null and num_nonnulls(episode_id, meal_item_id, change_id, food_id) = 0 when 'exposure' then num_nonnulls(meal_item_id, food_id) <= 1 and num_nonnulls(episode_id, change_id, confounder) = 0 when 'unknown' then num_nonnulls(episode_id, meal_item_id, change_id, food_id, confounder) = 0 end)

Library

library_items9 fieldsReference only. Not scored, ranked or rated, and never used as context for AI analysis.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
titletextrequired
categorytextrequired essential_oilprotocollaserhomeopathicdietsupplementtherapyother
tagslist of textrequired default '{}'
sourcetext
bodytext
created_atdate and timedefault now
updated_atdate and timedefault now

Linked from library_files.library_item_id, library_interventions.library_item_id

library_files4 fields
FieldTypeDetails
ididkey
library_item_ididrequired links to library_items (deleted with it)
storage_pathtextrequired
captiontext
library_interventions2 fields
FieldTypeDetails
library_item_ididlinks to library_items (deleted with it)
intervention_ididlinks to interventions
Table rules
  • primary key (library_item_id, intervention_id)

Measures and records

atec_scores9 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
scored_ondaterequired
speechwhole number
sociabilitywhole number
sensorywhole number
healthwhole number
totalwhole numbercomputed
Null unless all four are present. A missing subscale counted as zero would read as improvement, since lower ATEC is better.
notestext
growth5 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
measured_ondaterequired
weight_kgnumbercheck weight_kg > 0
height_cmnumbercheck height_cm > 0
test_results8 fields
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
test_typetextgeneticstoolbloodurineimagingassessmentreport
labtext
collected_ondate
summarytext
user’s own plain-language note
storage_pathtext
created_atdate and timedefault now

Linked from gene_variants.test_result_id

gene_variants6 fields
FieldTypeDetails
ididkey
test_result_ididrequired links to test_results (deleted with it)
genetext
varianttext
genotypetext
notestext

Monthly review

monthly_reviews8 fieldsPrefill, observation creation from new_or_odd, and progress_event creation from new gains/worse text are app logic, done once on submit.
FieldTypeDetails
ididkey
child_ididrequired links to children (deleted with it)
monthdaterequired first of the month
gainstext
worsetext
unchangedtext
new_or_oddtext
submitted_atdate and time
Table rules
  • unique (child_id, month)

Derived views

Nothing analytical is stored. Each view is worked out from the tables when it is read, so changing a setting (a multiplier, the illness recovery tail, the adherence threshold) updates past results too. Illness days are left out of before-and-after comparisons.

v_illness_days

Each day inside an illness episode or its recovery tail. An open episode runs to today; its tail starts once it is closed.

Reads illness_episodes children

v_meal_item_loads

Every meal item with its computed loads. item_load = coalesce(observed_score, histamine_seed) * portion_multiplier * freshness_multiplier Freshness only applies to perishable foods, and only to histamine; sugar and oxalate use their seed times portion.

Reads meal_items meals children foods daily_logs

v_daily_exposure

Daily exposure. Both the total and the largest single item are kept: a day can be harmful by accumulation or by one large exposure. The 48-hour figures are today plus yesterday. Sugar, histamine and oxalate are never combined.

Reads v_meal_item_loads

v_adherence

Adherence per intervention per month, over logged days only. Per change window adherence is in v_change_outcomes.

Reads v_daily_with_protocol

v_retest_candidates

Excluded foods ranked for retesting: confounded exclusions first, then those made before the most recent protocol change, then oldest first.

Reads changes foods

v_food_next_day

For any food, mean next-day measures after days it was eaten versus days it was not. Co-occurrence only; n is shown. "Not eaten" days require confirmed meals that day, so an unlogged day is never counted as a "no".

Reads v_meal_item_loads foods v_daily_measures_long

v_observed_scores

Proposed observed_score per food, from OCD on days after it was eaten versus not (lag set by children.observed_lag_days). The difference is mapped onto the 0–3 seed scale via children.observed_points_per_score. A proposal only: the app writes foods.observed_score after approval.

Reads v_meal_item_loads v_daily_measures_long foods children

v_exposure_lag

Each exposure measure correlated with each daily measure at 0, 1 and 2 day lags (0, 24 and 48 hours). Sugar and oxalate run independently.

Reads v_daily_exposure v_daily_measures_long

v_load_contributors

For the 5 highest histamine days each month, which items drove the number, ranked, with each item’s share of the day.

Reads v_daily_exposure v_meal_item_loads

v_variety

Distinct foods eaten per month, overall and per food group. Flags three consecutive months each lower than the one before.

Reads v_meal_item_loads

v_monthly_rollup

Per measure per month: total (meaningful for counts), daily mean, and change against the previous logged month. Daily means are compared, so a month with fewer logged days is not penalised.

Reads v_daily_measures_long

v_monthly_extras

The monthly figures that are not a single measure: stool types, zero-waking nights, accident-free days and the longest run of them. A run is broken by an accident or by an unlogged day.

Reads stool_events daily_logs

v_growth_trend

Weight and height with change since the previous measurement. Flags a plateau or drop across two consecutive measurements.

Reads growth

v_trigger_frequency

How often each trigger appears against setbacks, with mean magnitude and mean days to recovery. Rows with is_kind_total = true are totals per trigger kind; the rest are per specific food, change or confounder.

Reads v_event_trigger_labels progress_events

Rules the database enforces

One change at a time

A protocol change opens a window, 5 days by default. A second change whose window overlaps is refused unless overridden, and an overridden change is marked as overlapping. Both changes then show as confounded in the analysis.

Clean food trials

A trial starts clean only when: no illness is open or ended in the last 14 days, no change window is open, no other trial is open, the 48-hour histamine load is below its 48-hour amber threshold, and the 3 days before were logged. A trial that fails still runs, marked not clean, with the reasons saved.

Meal flags name their cause

Red for a day total, a single large item, a 48-hour total at or above its threshold, or a food on the known-reaction list. Amber for a day total at or above amber, or a high-histamine food at a normal or large portion. Histamine, sugar and oxalate are checked separately and never combined.

Adherence is a fact

What was prescribed and what was given are recorded separately. Only missed or changed doses need entering. A change window under 80% adherence is marked unreliable.

Private by default

Every table only shows the signed-in owner’s rows. Photos and PDFs sit in a private storage area, served by signed links only.

Nothing written automatically

Doses read from a protocol document by AI, foods read from a meal photo, and food scores proposed from his own data are all shown for confirmation before anything is saved.

Functions

set_updated_at runs automatically
Keeps the updated time on library items and thresholds current.
check_change_window runs automatically
Runs on every new change. Refuses a change whose window overlaps another change unless the override is passed, and then marks it as overlapping.
record_change
Records a protocol change in one step: ends the current entry, starts the new one and saves the change. If the change is refused, nothing is saved.
edit_change
Corrects a recorded change after the fact: its start date and how many days to watch. The protocol moves with it.
food_trial_gate_failures
Lists which start conditions a food trial would fail on a given date, so the app can show them before the trial begins.
set_food_trial_gate runs automatically
Runs on every new food trial and sets whether the start was clean. The app cannot set this itself.
evaluate_meal
Checks a meal against the thresholds. Returns one line per reason, each naming the cause, the number, and whether the score came from his own data or a published list.
export_child_json
Everything for one child as a single JSON file, saved locally once a day so the data is never trapped in the app.
owns_child
Used by the access rules: true only when the signed-in user owns that child profile.

How it was tested

The migrations were run on Postgres 17 against eight weeks of made-up data: 53 days of logs with one gap, five protocol changes, two meals a day, an illness episode, food trials and setbacks with linked causes.

  • All 32 tables, 24 views and 8 functions install with no errors.
  • Every view returns the expected rows, with values checked by hand.
  • An overlapping change is refused and leaves the protocol untouched. With the override it is saved and marked.
  • A window with 3 of 5 doses missed shows 40% adherence and is marked unreliable.
  • A trial saved as clean by the app is corrected to the real gate result.
  • A second account sees none of the first account’s data and cannot add to or change it.