In development

Bodivo

A training and nutrition app that learns how your body actually responds instead of handing you a fixed plan, and tells you plainly when it does not yet know enough to say.

Adaptive fitness and nutrition — it models the individual rather than the plan, and says so when it does not know.

Platform
Web
Status
In development
Demonstrates
Evidence-typed state

02

What it is for

For one person at first, and built multi-tenant from the beginning anyway.

A training app hands you a plan. The plan does not know that you slept badly, that the last three sessions went better than it expected, or that it has no data on you at all yet. Bodivo is built to answer three questions the moment it opens: where am I, how am I doing, and what should I do next. The weight is on the last one. The part I care about is that it refuses to answer any of the three when the evidence is not there.

03

Why it exists

A training plan is a guess made once, in advance, by someone who has never met you.

The apps that replaced the paper plan mostly kept its shape and added charts — and to fill those charts they render a zero where they have no reading, draw a trend through two points, and call a week of noise progress. Each is a small lie and the aggregate is why nobody trusts them. The people with the problem are anyone training seriously enough to want the plan to respond to them.

04

Screens

Bodivo's Intelligence screen on a new account, reading "Bodivo has nothing evidence-backed to say about you yet" and "0 of 6 logged" beside what unlocks learning.
01what it believes about you, and what it is still missing
Bodivo's System screen listing health checks, several marked NO DATA with the reason: no completed session on record, no weigh-ins, and nutrition-dependent guidance withheld rather than estimated.
02every check says what is missing, not zero
Bodivo's Calibration screen headed "What Bodivo still needs from you", listing the assessments it wants before it will prescribe anything.
03what would fill it, stated as a list
Bodivo's Today screen with demo data loaded, showing the day's session, the phase, and body weight against planned rate, labelled DEMO DATA.
04the same app once there is something to say

05

What it does

  • Provenance-typed metrics

    Every value declares measured, calculated or unknown. An unknown is never rendered as zero.

  • Adaptive challenge

    A daily verdict — push, progress, hold or recover — from typed, signed signals across seven families.

  • Goal-shaped progress

    What counts as progress for a marathon block is not what counts for a lean bulk, and the model reshapes accordingly.

  • Evidence-backed characteristics

    A belief about the person and the evidence for it are separate rows; one cannot exist without the other.

  • Calibration

    Baseline assessment that establishes what is actually known before anything is prescribed.

  • Agent governance contract

    Teams, tool registry, compute tiers and reach limits, declared before any model runtime is connected.

  • RLS tested in SQL

    Row-level security and migration safety verified by a SQL test suite, not only by application tests.

  • Nutrition and recovery

    Meals, macros, hydration, sleep, mood and heart rate, all feeding the same evidence layer.

06

What it runs on

Client

01
React 19
02
TanStack Start
03
TanStack Router
04
shadcn/ui
05
Tailwind

Backend

01
Supabase Postgres
02
55 tables
03
36 migrations
04
SQL-level RLS tests

Services

01
Vitest
02
Playwright
03
No model provider — by design

07

How it's built

TanStack Start with server-side rendering over Supabase Postgres — fifty-five tables across thirty-six migrations. The layering is the architecture: around thirty domain modules are pure, with no database access, no clock reads and no randomness, because today is passed in as a parameter. Only files suffixed .server.ts touch the database. Seventy-eight test files sit against that pure core, which is what makes forty-odd thousand lines of rules maintainable.

The belief system is two tables rather than one: a characteristic and the evidence for it are separate rows, so a claim about the person always has something behind it. Proposals are recorded as experiments and checked against what happened.

There is no language model anywhere in the running system. The agent topology — teams, specialists, a governed tool registry, and a compute ladder where deterministic work may never be sent to a model — is declared as a typed contract with no runtime attached, and the app reports that honestly on its own system page.

08

Architecture

One figure, read left to right. Border treatment carries the node type; the color is the project's.

with provenancetrajectorytoday's verdictproposesvalidated or notpropose onlyhealth_observationsSleep, HR, weight, moodworkout_sessionsset_records, rest_intervalscharacteristic_evidenceWhat supports each beliefcharacteristics.tsMeasured, calculated, unknownprogress-model.tssufficient: false when thinadaptive-challenge.tsPush, progress, hold, recoverplan-composition.tsprogram_versionsexperimentsvalidation_resultsagents.ts registryRuntime not connected
Fig. 1evidence, belief, and the daily decision
  • Service
  • Store
  • External
  • Model

09

A piece of the code

src/lib/domain/agents.ts

/** No model runtime is wired to this registry yet. Surfaced honestly in /system. */export const AGENT_RUNTIME_CONNECTED = false;/** * Where the work belongs on the cost/quality ladder. * * Deterministic work must never be sent to a model: it is arithmetic with a * right answer. Cached is derived state recomputed on an event, not per * view. Light handles language, classification and summarising. * Reasoning is reserved for genuinely open-ended synthesis over conflicting * evidence, and is always triggered by an event with an evidence floor met. */export type ComputeTier = "deterministic" | "cached" | "light" | "reasoning";/** What a specialist may do at most. Never widened by an autonomy setting. */export type AgentReach = "read_only" | "propose_only" | "propose_and_execute";
Decide what a model is allowed to touch before you are invested in it touching things. The constant is exported and rendered on the app's own system page, so the product tells its user that the agent layer is not live rather than implying intelligence it does not have.

Excerpt — trimmed for reading, not a full file.

10

The hard parts

01Problem

Every fitness app renders a zero when it has no data. A weight chart starting at zero, a trend line drawn through two points, an encouraging verdict from a week of noise. Each is a lie, and the aggregate is why nobody trusts these apps.

Approach

Provenance is part of the value rather than metadata about it. Every metric declares measured, calculated or unknown; a trend carries its sample count, its span, whether it is sufficient, whether it is stale, and a plain statement of what was observed or what is missing. A view with no evidence returns why, and what would fill it.

Tradeoff

The interface is harder to build and, early on, emptier. A new user sees a lot of "not enough evidence yet" where a competitor shows a confident zero. That is the correct trade and it is also why the product looks sparse.

02Problem

The daily verdict has to come from signals that are always partly missing. The naive system defaults to recovery whenever data is thin, which produces a coach that tells you to rest forever.

Approach

Recovery is never the default: it requires a hard constraint or at least two corroborating signals. Injury and illness win outright. Missing data lowers confidence and never forces a conservative verdict. Being behind trajectory biases toward pushing when capacity supports it; being ahead caps the verdict rather than licensing acceleration. Every decision reports the signals it used and the ones it did not have.

Tradeoff

Deliberately asymmetric — it is easier to reach push from thin evidence than recovery. That is a risk posture, taken because a system that quietly under-trains someone is worse than one that occasionally over-reaches.

03Problem

A constraint someone recorded but never rated in severity. The original schema stored it as medium, which is unreadable: is that their judgement, or a default nobody set?

Approach

Unstated became its own value, neither blocking nor limiting. It guards nothing and contributes only a guardrail line.

Tradeoff

Three-state severity everywhere it is consumed rather than two. Promoting silence to a hard block would have turned every constraint ever recorded into a forced rest day.

04Problem

The usual order is to call the model, see whether it is useful, and add guardrails after something goes wrong. By then the model's reach is load-bearing and narrowing it breaks features.

Approach

The topology, the tool registry, the compute tiers and the reach limits are declared as a typed contract with no runtime. Deterministic work — arithmetic with a right answer — may never be sent to a model. A specialist's reach is never widened by an autonomy setting. The app exports the fact that no runtime is connected and shows it on its own system page.

Tradeoff

A large file that currently executes nothing. If the runtime never arrives it is dead weight, and that is a real risk this early.

Ask about Bodivo

Answers are drawn from the notes and source behind this case study, and cite what they read.