Vibecode MacroFactor
track this build5 phases, 15 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For MacroFactor, record user-chosen nutrition information and show neutral trends without aggressive targets. The hard boundary is food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise, plus hardware data, content, coaching, and trust.
You are building a lean indie version of MacroFactor. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # MacroFactor · indie build A nutrition tracker whose distinguishing feature is that it never uses a formula for your energy expenditure. It derives expenditure from the relationship between what you logged and how your weight trend actually moved, which means the interesting work is separating signal from noise in daily weight and being honest about the error you cannot remove. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a local application and SQLite | food and weight logs are health data and belong on your own machine | | Trend | An exponential moving average over daily weights | a single day's weight is mostly water and cannot be used for anything | | Expenditure | Intake minus the energy implied by the trend change, over a rolling window | this is the whole idea, and it needs weeks of data before it means anything | | Food data | A local food database you import, with your own entries on top | the database is the boring part and the part that determines whether anyone uses it | | Safety | Floors and rate caps enforced before any recommendation is shown | the algorithm must not be able to produce an unsafe target | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **Your floor, your rate cap and who this is not for** · free - Why: These are enforced in code before anything is displayed, and deciding them after the algorithm exists means deciding them to fit the algorithm. - Get it: Write down an absolute minimum daily intake, a maximum weekly rate of change, and a plain statement that this tool is not appropriate for anyone with a history of disordered eating. - Verify: All three are written down and the first two appear as constants in the safety module - [ ] **A food database you may use** · free for public datasets; commercial databases are licensed - Why: Search over a local database is what makes logging fast enough to keep happening, and licence terms differ sharply between sources. - Get it: A publicly available national nutrient database, checking the licence covers your use. Import it locally rather than querying a service. - Verify: Searching returns results offline in under a hundred milliseconds - [ ] **A scale and eight weeks of your own data** · free if you own a scale, and it takes eight weeks - Why: Expenditure estimation cannot be evaluated on synthetic data alone, and the minimum useful window is measured in weeks rather than days. - Get it: Weigh daily at a consistent time and log intake honestly, including the days you would rather not. - Verify: Eight weeks of weights and mostly complete intake logs exist ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. - food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise - wearable hardware data - coach network - clinical validation - large content library and social network If one of those is essential to you, that is the reason to keep paying for MacroFactor, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · MacroFactor The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a nutrition tracker that works out your actual energy expenditure from your own weight and intake data to replace MacroFactor. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This tool asks someone to weigh themselves daily and log everything they eat, and then it tells them a number to eat. That combination is used by people with eating disorders and it makes them worse. Build the safeguards before the algorithm. Concretely: a floor below which the target will not go regardless of what the maths says, a cap on the rate of loss the tool will plan for, no streak counters or guilt language for a missed day, and no scoring of individual foods as good or bad. If a user sets a goal below the floor the tool refuses and says why. The other honest limit is that this estimates expenditure from logged intake, so it is only as accurate as the logging. Someone under-reporting will be told their expenditure is low and given a lower target, which is a loop that ends badly. Say this in the interface, not only in the README. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a local application and SQLite · food and weight logs are health data and belong on your own machine - Trend: An exponential moving average over daily weights · a single day's weight is mostly water and cannot be used for anything - Expenditure: Intake minus the energy implied by the trend change, over a rolling window · this is the whole idea, and it needs weeks of data before it means anything - Food data: A local food database you import, with your own entries on top · the database is the boring part and the part that determines whether anyone uses it - Safety: Floors and rate caps enforced before any recommendation is shown · the algorithm must not be able to produce an unsafe target ### Data model (create this before Phase 1) - weights(date, kg, entered_at) · one per day, and days are allowed to be missing - trend(date, kg) · derived, never entered, an exponential moving average over the raw series - foods(id, name, kcal_per_100g, protein_g, carb_g, fat_g, source) · source is imported or user - entries(id, date, food_id, grams, meal, logged_at) · the log, kept even when a day is incomplete - expenditure(date, kcal, window_days, confidence) · derived, with the window it was computed over - goals(id, started_at, direction, rate_kg_per_week, floor_kcal, ended_at) · the rate cap and floor live here - Expenditure is a derived table with the window recorded, because a number computed over eight days and one computed over eight weeks are not the same claim. ### Phase 1 · A weight trend that is not the scale reading Build: Daily weight entry and an exponential moving average trend over it. Handle missing days without breaking the smoothing. Show raw and trend together so the difference is visible. Trap: Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. Done when: A daily series produces a trend that visibly lags and smooths the raw values. Missing days do not create a discontinuity, tested with a week of gaps. Raw and trend display together. A single large outlier moves the trend slightly rather than dominating it. Do not build yet: No intake, expenditure or goals. ### Phase 2 · Logging food without it being a chore Build: A local food database with search, custom foods, saved meals and a repeat-yesterday action. Log by portion as well as by weight. Measure how long a realistic day takes and treat it as a requirement. Trap: A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. Done when: A realistic day logs in under two minutes, timed by hand. Saved meals and repeat-yesterday both work. Custom foods can be created in the flow rather than in a separate screen. A partially logged day is marked as incomplete rather than counted as a low intake day. Do not build yet: No expenditure calculation yet. ### Phase 3 · Expenditure from your own data Build: Compute expenditure over a rolling window as average intake minus the energy implied by the trend change. Exclude incomplete days. Report a confidence that reflects the window length and the amount of logged data, and refuse to show a number below a minimum window. Trap: Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. Done when: Expenditure computes over a rolling window from complete days only. Fewer days than the minimum shows no number and says why, tested by entering one week of data. The confidence reflects window length and completeness. A synthetic dataset with a known expenditure recovers it within a stated tolerance, asserted in a test. Do not build yet: No targets or goals yet. ### Phase 4 · Targets with a floor the maths cannot cross Build: Derive a daily target from expenditure and a chosen rate of change. Enforce an absolute floor and a maximum rate of loss before the number is shown. Refuse goals that would require crossing either, and explain rather than silently clamping. Trap: Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. Done when: A target derives from expenditure and the chosen rate. A goal below the floor is refused with an explanation, tested directly. A rate above the cap is refused. The floor cannot be crossed by any combination of inputs, verified by property testing across a range of values. Do not build yet: No adaptation or review yet. ### Phase 5 · Adapting without chasing noise Build: Update the target on a fixed cadence rather than continuously, limit how far it can move in one update, and show what changed and why. Detect the case where logging quality has dropped and pause adaptation instead of adapting to bad data. Trap: Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. Done when: The target updates on a weekly cadence with a limited step size. A drop in logging completeness pauses adaptation and says so, tested by degrading the log. Every update shows the previous value, the new value and the reason. An implausible week-over-week expenditure change is flagged rather than applied. Do not build yet: Review and export come last. ### Phase 6 · Review, export and deleting it all Build: A review showing trend, expenditure and adherence over months without scoring the user. Full export of weights, entries and derived series. Complete deletion including derived tables and backups. Trap: A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. Done when: The review shows trend, expenditure and intake over months with no score, grade or streak anywhere in it, verified by reading the whole surface. Export contains raw and derived data and re-imports identically. Deletion removes entries, weights, derived tables and any local backup. Do not build yet: Nothing after this. ### Out of scope (and why) - Barcode scanning and photo food recognition, which is most of what makes a commercial tracker fast to use. - A comprehensive maintained food database, which is a data licensing and curation operation rather than a feature. - Any clinical claim. This is not a medical device and cannot be used to manage a diagnosed condition. - Coaching, macronutrient periodisation and training integration, which the hosted product builds on top of the same expenditure model. ### README must contain - That the safety floor and rate cap are enforced in the computation and cannot be crossed - That expenditure is estimated from your logging, so under-reporting produces a low estimate and a lower target - That daily weight is mostly water and only the trend is used - That no expenditure figure is shown before a minimum window of data - That there are no streaks, scores or food grades anywhere in the interface ===== AGENTS.md ===== # Agent instructions · MacroFactor indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with a local application and SQLite, An exponential moving average over daily weights, Intake minus the energy implied by the trend change, over a rolling window, A local food database you import, with your own entries on top, Floors and rate caps enforced before any recommendation is shown. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ===== BUILD_PLAN.md ===== # Build plan · MacroFactor A nutrition tracker whose distinguishing feature is that it never uses a formula for your energy expenditure. It derives expenditure from the relationship between what you logged and how your weight trend actually moved, which means the interesting work is separating signal from noise in daily weight and being honest about the error you cannot remove. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · A weight trend that is not the scale reading You can see the signal in a noisy daily weight series. ### Steps 1. Smooth with an exponential moving average A pure function over the series, unit tested with a synthetic step change. Files: `src/trend.ts`, `src/trend.test.ts` 2. Handle missing days explicitly People skip days. The smoothing must not assume a dense series. Files: `src/trend.ts` 3. Show raw against trend So the user sees how little a single day means. This is educational, not decorative. Files: `src/chart.ts` ### Done when - [ ] A trend smooths and lags the raw series - [ ] A week of missing days does not break smoothing - [ ] Raw and trend display together - [ ] One outlier does not dominate the trend ### Watch out - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. ## Phase 2 · Logging food without it being a chore A normal day's food is logged in a couple of minutes. ### Steps 1. Import a food database locally Search must be fast offline. This is the unglamorous part that decides adoption. Files: `src/foods.ts` 2. Saved meals and repeat yesterday Most people eat the same things. Exploit that or the logging stops. Files: `src/quick.ts` 3. Mark incomplete days Never treat a half-logged day as a low intake day. It poisons every later number. Files: `src/entries.ts` ### Done when - [ ] A realistic day logs in under two minutes - [ ] Saved meals and repeat-yesterday work - [ ] Custom foods can be added inline - [ ] An incomplete day is marked, not counted as low ### Watch out - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. ## Phase 3 · Expenditure from your own data The tool tells you what you actually burn, with an honest confidence. ### Steps 1. Compute over a rolling window Average intake minus energy implied by trend change. Excluding incomplete days. Files: `src/expenditure.ts` 2. Refuse below a minimum window No number is better than a number that is wrong by a thousand. Files: `src/expenditure.ts` 3. Validate against a synthetic dataset Known expenditure in, recovered expenditure out, within a stated tolerance. Files: `src/expenditure.test.ts` ### Done when - [ ] Expenditure computes from complete days over a window - [ ] Too little data shows no number and explains why - [ ] Confidence reflects window length and completeness - [ ] A synthetic dataset recovers its known expenditure ### Watch out - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. ## Phase 4 · Targets with a floor the maths cannot cross A recommended intake exists and it is never unsafe. ### Steps 1. Derive the target from measured expenditure Not from a formula. That is the entire premise of the tool. Files: `src/target.ts` 2. Enforce floor and rate cap before display In the computation, not the interface. Property test that nothing crosses them. Files: `src/safety.ts`, `src/safety.test.ts` 3. Refuse and explain rather than clamp A silent clamp looks like agreement and the user tries again. Files: `src/goals.ts` ### Done when - [ ] A target derives from measured expenditure - [ ] A goal below the floor is refused with a reason - [ ] A rate above the cap is refused - [ ] Property testing finds no input that crosses the floor ### Watch out - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. ## Phase 5 · Adapting without chasing noise The target updates weekly as your expenditure changes, and does not thrash. ### Steps 1. Update on a cadence with a step limit Weekly, bounded. Continuous adaptation chases water weight. Files: `src/adapt.ts` 2. Pause on degraded logging The spiral this prevents is the most important behaviour in the tool. Files: `src/adapt.ts` 3. Explain every change Previous, new, and why. An unexplained change in a health target erodes trust. Files: `src/adapt.ts` ### Done when - [ ] The target updates weekly with a bounded step - [ ] Degraded logging pauses adaptation - [ ] Every update shows its reason - [ ] An implausible expenditure change is flagged ### Watch out - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ## Not in this build - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. ## After v1, if you want it - Protein and fibre targets alongside energy, which most people need more than precision on calories - Recomposition handling, where weight barely moves and the expenditure model needs care - A diet break mode that pauses goals without pausing measurement ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Weights and food logs. Health data, so treat backups accordingly. DB_PATH=~/.local/share/macrofactor/data.db # Required. The absolute minimum target. Enforced in the safety module, not the interface. FLOOR_KCAL=1500 # Required. The fastest change the tool will plan for. Goals beyond it are refused. MAX_RATE_KG_PER_WEEK=0.75 # Required. Below this no expenditure figure is shown at all, because it would be noise. MIN_WINDOW_DAYS=21 # Required. How fast the trend responds. Shorter chases water, longer lags real change. TREND_HALF_LIFE_DAYS=10
You are building a lean indie version of MacroFactor. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # MacroFactor · indie build A nutrition tracker whose distinguishing feature is that it never uses a formula for your energy expenditure. It derives expenditure from the relationship between what you logged and how your weight trend actually moved, which means the interesting work is separating signal from noise in daily weight and being honest about the error you cannot remove. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a local application and SQLite | food and weight logs are health data and belong on your own machine | | Trend | An exponential moving average over daily weights | a single day's weight is mostly water and cannot be used for anything | | Expenditure | Intake minus the energy implied by the trend change, over a rolling window | this is the whole idea, and it needs weeks of data before it means anything | | Food data | A local food database you import, with your own entries on top | the database is the boring part and the part that determines whether anyone uses it | | Safety | Floors and rate caps enforced before any recommendation is shown | the algorithm must not be able to produce an unsafe target | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **Your floor, your rate cap and who this is not for** · free - Why: These are enforced in code before anything is displayed, and deciding them after the algorithm exists means deciding them to fit the algorithm. - Get it: Write down an absolute minimum daily intake, a maximum weekly rate of change, and a plain statement that this tool is not appropriate for anyone with a history of disordered eating. - Verify: All three are written down and the first two appear as constants in the safety module - [ ] **A food database you may use** · free for public datasets; commercial databases are licensed - Why: Search over a local database is what makes logging fast enough to keep happening, and licence terms differ sharply between sources. - Get it: A publicly available national nutrient database, checking the licence covers your use. Import it locally rather than querying a service. - Verify: Searching returns results offline in under a hundred milliseconds - [ ] **A scale and eight weeks of your own data** · free if you own a scale, and it takes eight weeks - Why: Expenditure estimation cannot be evaluated on synthetic data alone, and the minimum useful window is measured in weeks rather than days. - Get it: Weigh daily at a consistent time and log intake honestly, including the days you would rather not. - Verify: Eight weeks of weights and mostly complete intake logs exist ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. - food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise - wearable hardware data - coach network - clinical validation - large content library and social network If one of those is essential to you, that is the reason to keep paying for MacroFactor, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · MacroFactor The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a nutrition tracker that works out your actual energy expenditure from your own weight and intake data to replace MacroFactor. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This tool asks someone to weigh themselves daily and log everything they eat, and then it tells them a number to eat. That combination is used by people with eating disorders and it makes them worse. Build the safeguards before the algorithm. Concretely: a floor below which the target will not go regardless of what the maths says, a cap on the rate of loss the tool will plan for, no streak counters or guilt language for a missed day, and no scoring of individual foods as good or bad. If a user sets a goal below the floor the tool refuses and says why. The other honest limit is that this estimates expenditure from logged intake, so it is only as accurate as the logging. Someone under-reporting will be told their expenditure is low and given a lower target, which is a loop that ends badly. Say this in the interface, not only in the README. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a local application and SQLite · food and weight logs are health data and belong on your own machine - Trend: An exponential moving average over daily weights · a single day's weight is mostly water and cannot be used for anything - Expenditure: Intake minus the energy implied by the trend change, over a rolling window · this is the whole idea, and it needs weeks of data before it means anything - Food data: A local food database you import, with your own entries on top · the database is the boring part and the part that determines whether anyone uses it - Safety: Floors and rate caps enforced before any recommendation is shown · the algorithm must not be able to produce an unsafe target ### Data model (create this before Phase 1) - weights(date, kg, entered_at) · one per day, and days are allowed to be missing - trend(date, kg) · derived, never entered, an exponential moving average over the raw series - foods(id, name, kcal_per_100g, protein_g, carb_g, fat_g, source) · source is imported or user - entries(id, date, food_id, grams, meal, logged_at) · the log, kept even when a day is incomplete - expenditure(date, kcal, window_days, confidence) · derived, with the window it was computed over - goals(id, started_at, direction, rate_kg_per_week, floor_kcal, ended_at) · the rate cap and floor live here - Expenditure is a derived table with the window recorded, because a number computed over eight days and one computed over eight weeks are not the same claim. ### Phase 1 · A weight trend that is not the scale reading Build: Daily weight entry and an exponential moving average trend over it. Handle missing days without breaking the smoothing. Show raw and trend together so the difference is visible. Trap: Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. Done when: A daily series produces a trend that visibly lags and smooths the raw values. Missing days do not create a discontinuity, tested with a week of gaps. Raw and trend display together. A single large outlier moves the trend slightly rather than dominating it. Do not build yet: No intake, expenditure or goals. ### Phase 2 · Logging food without it being a chore Build: A local food database with search, custom foods, saved meals and a repeat-yesterday action. Log by portion as well as by weight. Measure how long a realistic day takes and treat it as a requirement. Trap: A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. Done when: A realistic day logs in under two minutes, timed by hand. Saved meals and repeat-yesterday both work. Custom foods can be created in the flow rather than in a separate screen. A partially logged day is marked as incomplete rather than counted as a low intake day. Do not build yet: No expenditure calculation yet. ### Phase 3 · Expenditure from your own data Build: Compute expenditure over a rolling window as average intake minus the energy implied by the trend change. Exclude incomplete days. Report a confidence that reflects the window length and the amount of logged data, and refuse to show a number below a minimum window. Trap: Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. Done when: Expenditure computes over a rolling window from complete days only. Fewer days than the minimum shows no number and says why, tested by entering one week of data. The confidence reflects window length and completeness. A synthetic dataset with a known expenditure recovers it within a stated tolerance, asserted in a test. Do not build yet: No targets or goals yet. ### Phase 4 · Targets with a floor the maths cannot cross Build: Derive a daily target from expenditure and a chosen rate of change. Enforce an absolute floor and a maximum rate of loss before the number is shown. Refuse goals that would require crossing either, and explain rather than silently clamping. Trap: Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. Done when: A target derives from expenditure and the chosen rate. A goal below the floor is refused with an explanation, tested directly. A rate above the cap is refused. The floor cannot be crossed by any combination of inputs, verified by property testing across a range of values. Do not build yet: No adaptation or review yet. ### Phase 5 · Adapting without chasing noise Build: Update the target on a fixed cadence rather than continuously, limit how far it can move in one update, and show what changed and why. Detect the case where logging quality has dropped and pause adaptation instead of adapting to bad data. Trap: Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. Done when: The target updates on a weekly cadence with a limited step size. A drop in logging completeness pauses adaptation and says so, tested by degrading the log. Every update shows the previous value, the new value and the reason. An implausible week-over-week expenditure change is flagged rather than applied. Do not build yet: Review and export come last. ### Phase 6 · Review, export and deleting it all Build: A review showing trend, expenditure and adherence over months without scoring the user. Full export of weights, entries and derived series. Complete deletion including derived tables and backups. Trap: A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. Done when: The review shows trend, expenditure and intake over months with no score, grade or streak anywhere in it, verified by reading the whole surface. Export contains raw and derived data and re-imports identically. Deletion removes entries, weights, derived tables and any local backup. Do not build yet: Nothing after this. ### Out of scope (and why) - Barcode scanning and photo food recognition, which is most of what makes a commercial tracker fast to use. - A comprehensive maintained food database, which is a data licensing and curation operation rather than a feature. - Any clinical claim. This is not a medical device and cannot be used to manage a diagnosed condition. - Coaching, macronutrient periodisation and training integration, which the hosted product builds on top of the same expenditure model. ### README must contain - That the safety floor and rate cap are enforced in the computation and cannot be crossed - That expenditure is estimated from your logging, so under-reporting produces a low estimate and a lower target - That daily weight is mostly water and only the trend is used - That no expenditure figure is shown before a minimum window of data - That there are no streaks, scores or food grades anywhere in the interface ===== AGENTS.md ===== # Agent instructions · MacroFactor indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with a local application and SQLite, An exponential moving average over daily weights, Intake minus the energy implied by the trend change, over a rolling window, A local food database you import, with your own entries on top, Floors and rate caps enforced before any recommendation is shown. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ===== BUILD_PLAN.md ===== # Build plan · MacroFactor A nutrition tracker whose distinguishing feature is that it never uses a formula for your energy expenditure. It derives expenditure from the relationship between what you logged and how your weight trend actually moved, which means the interesting work is separating signal from noise in daily weight and being honest about the error you cannot remove. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · A weight trend that is not the scale reading You can see the signal in a noisy daily weight series. ### Steps 1. Smooth with an exponential moving average A pure function over the series, unit tested with a synthetic step change. Files: `src/trend.ts`, `src/trend.test.ts` 2. Handle missing days explicitly People skip days. The smoothing must not assume a dense series. Files: `src/trend.ts` 3. Show raw against trend So the user sees how little a single day means. This is educational, not decorative. Files: `src/chart.ts` ### Done when - [ ] A trend smooths and lags the raw series - [ ] A week of missing days does not break smoothing - [ ] Raw and trend display together - [ ] One outlier does not dominate the trend ### Watch out - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. ## Phase 2 · Logging food without it being a chore A normal day's food is logged in a couple of minutes. ### Steps 1. Import a food database locally Search must be fast offline. This is the unglamorous part that decides adoption. Files: `src/foods.ts` 2. Saved meals and repeat yesterday Most people eat the same things. Exploit that or the logging stops. Files: `src/quick.ts` 3. Mark incomplete days Never treat a half-logged day as a low intake day. It poisons every later number. Files: `src/entries.ts` ### Done when - [ ] A realistic day logs in under two minutes - [ ] Saved meals and repeat-yesterday work - [ ] Custom foods can be added inline - [ ] An incomplete day is marked, not counted as low ### Watch out - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. ## Phase 3 · Expenditure from your own data The tool tells you what you actually burn, with an honest confidence. ### Steps 1. Compute over a rolling window Average intake minus energy implied by trend change. Excluding incomplete days. Files: `src/expenditure.ts` 2. Refuse below a minimum window No number is better than a number that is wrong by a thousand. Files: `src/expenditure.ts` 3. Validate against a synthetic dataset Known expenditure in, recovered expenditure out, within a stated tolerance. Files: `src/expenditure.test.ts` ### Done when - [ ] Expenditure computes from complete days over a window - [ ] Too little data shows no number and explains why - [ ] Confidence reflects window length and completeness - [ ] A synthetic dataset recovers its known expenditure ### Watch out - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. ## Phase 4 · Targets with a floor the maths cannot cross A recommended intake exists and it is never unsafe. ### Steps 1. Derive the target from measured expenditure Not from a formula. That is the entire premise of the tool. Files: `src/target.ts` 2. Enforce floor and rate cap before display In the computation, not the interface. Property test that nothing crosses them. Files: `src/safety.ts`, `src/safety.test.ts` 3. Refuse and explain rather than clamp A silent clamp looks like agreement and the user tries again. Files: `src/goals.ts` ### Done when - [ ] A target derives from measured expenditure - [ ] A goal below the floor is refused with a reason - [ ] A rate above the cap is refused - [ ] Property testing finds no input that crosses the floor ### Watch out - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. ## Phase 5 · Adapting without chasing noise The target updates weekly as your expenditure changes, and does not thrash. ### Steps 1. Update on a cadence with a step limit Weekly, bounded. Continuous adaptation chases water weight. Files: `src/adapt.ts` 2. Pause on degraded logging The spiral this prevents is the most important behaviour in the tool. Files: `src/adapt.ts` 3. Explain every change Previous, new, and why. An unexplained change in a health target erodes trust. Files: `src/adapt.ts` ### Done when - [ ] The target updates weekly with a bounded step - [ ] Degraded logging pauses adaptation - [ ] Every update shows its reason - [ ] An implausible expenditure change is flagged ### Watch out - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ## Not in this build - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. ## After v1, if you want it - Protein and fibre targets alongside energy, which most people need more than precision on calories - Recomposition handling, where weight barely moves and the expenditure model needs care - A diet break mode that pauses goals without pausing measurement ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Weights and food logs. Health data, so treat backups accordingly. DB_PATH=~/.local/share/macrofactor/data.db # Required. The absolute minimum target. Enforced in the safety module, not the interface. FLOOR_KCAL=1500 # Required. The fastest change the tool will plan for. Goals beyond it are refused. MAX_RATE_KG_PER_WEEK=0.75 # Required. Below this no expenditure figure is shown at all, because it would be noise. MIN_WINDOW_DAYS=21 # Required. How fast the trend responds. Shorter chases water, longer lags real change. TREND_HALF_LIFE_DAYS=10
You are building a production product version of MacroFactor. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # MacroFactor · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For MacroFactor, record user-chosen nutrition information and show neutral trends without aggressive targets. The hard boundary is food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise, plus hardware data, content, coaching, and trust. ## Product outcome A tracker that measures your energy expenditure instead of guessing it from a formula, refuses to show a number it cannot support with data, and cannot be made to recommend an unsafe target. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - iOS or Android device - optional HealthKit or Health Connect permission - local storage ## Explicit non-goals for v1 - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. - food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise - wearable hardware data - coach network - clinical validation - large content library and social network ## Success criteria - Property testing is confirmed to find no input crossing the floor - An expenditure figure is confirmed absent below the minimum window - Degraded logging is confirmed to pause adaptation - The review surface is confirmed to contain no score, grade or streak - The synthetic dataset is confirmed to recover its known expenditure ===== BRIEF.md ===== # Build brief · MacroFactor The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a nutrition tracker that works out your actual energy expenditure from your own weight and intake data to replace MacroFactor. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This tool asks someone to weigh themselves daily and log everything they eat, and then it tells them a number to eat. That combination is used by people with eating disorders and it makes them worse. Build the safeguards before the algorithm. Concretely: a floor below which the target will not go regardless of what the maths says, a cap on the rate of loss the tool will plan for, no streak counters or guilt language for a missed day, and no scoring of individual foods as good or bad. If a user sets a goal below the floor the tool refuses and says why. The other honest limit is that this estimates expenditure from logged intake, so it is only as accurate as the logging. Someone under-reporting will be told their expenditure is low and given a lower target, which is a loop that ends badly. Say this in the interface, not only in the README. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a local application and SQLite · food and weight logs are health data and belong on your own machine - Trend: An exponential moving average over daily weights · a single day's weight is mostly water and cannot be used for anything - Expenditure: Intake minus the energy implied by the trend change, over a rolling window · this is the whole idea, and it needs weeks of data before it means anything - Food data: A local food database you import, with your own entries on top · the database is the boring part and the part that determines whether anyone uses it - Safety: Floors and rate caps enforced before any recommendation is shown · the algorithm must not be able to produce an unsafe target ### Data model (create this before Phase 1) - weights(date, kg, entered_at) · one per day, and days are allowed to be missing - trend(date, kg) · derived, never entered, an exponential moving average over the raw series - foods(id, name, kcal_per_100g, protein_g, carb_g, fat_g, source) · source is imported or user - entries(id, date, food_id, grams, meal, logged_at) · the log, kept even when a day is incomplete - expenditure(date, kcal, window_days, confidence) · derived, with the window it was computed over - goals(id, started_at, direction, rate_kg_per_week, floor_kcal, ended_at) · the rate cap and floor live here - Expenditure is a derived table with the window recorded, because a number computed over eight days and one computed over eight weeks are not the same claim. ### Phase 1 · A weight trend that is not the scale reading Build: Daily weight entry and an exponential moving average trend over it. Handle missing days without breaking the smoothing. Show raw and trend together so the difference is visible. Trap: Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. Done when: A daily series produces a trend that visibly lags and smooths the raw values. Missing days do not create a discontinuity, tested with a week of gaps. Raw and trend display together. A single large outlier moves the trend slightly rather than dominating it. Do not build yet: No intake, expenditure or goals. ### Phase 2 · Logging food without it being a chore Build: A local food database with search, custom foods, saved meals and a repeat-yesterday action. Log by portion as well as by weight. Measure how long a realistic day takes and treat it as a requirement. Trap: A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. Done when: A realistic day logs in under two minutes, timed by hand. Saved meals and repeat-yesterday both work. Custom foods can be created in the flow rather than in a separate screen. A partially logged day is marked as incomplete rather than counted as a low intake day. Do not build yet: No expenditure calculation yet. ### Phase 3 · Expenditure from your own data Build: Compute expenditure over a rolling window as average intake minus the energy implied by the trend change. Exclude incomplete days. Report a confidence that reflects the window length and the amount of logged data, and refuse to show a number below a minimum window. Trap: Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. Done when: Expenditure computes over a rolling window from complete days only. Fewer days than the minimum shows no number and says why, tested by entering one week of data. The confidence reflects window length and completeness. A synthetic dataset with a known expenditure recovers it within a stated tolerance, asserted in a test. Do not build yet: No targets or goals yet. ### Phase 4 · Targets with a floor the maths cannot cross Build: Derive a daily target from expenditure and a chosen rate of change. Enforce an absolute floor and a maximum rate of loss before the number is shown. Refuse goals that would require crossing either, and explain rather than silently clamping. Trap: Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. Done when: A target derives from expenditure and the chosen rate. A goal below the floor is refused with an explanation, tested directly. A rate above the cap is refused. The floor cannot be crossed by any combination of inputs, verified by property testing across a range of values. Do not build yet: No adaptation or review yet. ### Phase 5 · Adapting without chasing noise Build: Update the target on a fixed cadence rather than continuously, limit how far it can move in one update, and show what changed and why. Detect the case where logging quality has dropped and pause adaptation instead of adapting to bad data. Trap: Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. Done when: The target updates on a weekly cadence with a limited step size. A drop in logging completeness pauses adaptation and says so, tested by degrading the log. Every update shows the previous value, the new value and the reason. An implausible week-over-week expenditure change is flagged rather than applied. Do not build yet: Review and export come last. ### Phase 6 · Review, export and deleting it all Build: A review showing trend, expenditure and adherence over months without scoring the user. Full export of weights, entries and derived series. Complete deletion including derived tables and backups. Trap: A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. Done when: The review shows trend, expenditure and intake over months with no score, grade or streak anywhere in it, verified by reading the whole surface. Export contains raw and derived data and re-imports identically. Deletion removes entries, weights, derived tables and any local backup. Do not build yet: Nothing after this. ### Out of scope (and why) - Barcode scanning and photo food recognition, which is most of what makes a commercial tracker fast to use. - A comprehensive maintained food database, which is a data licensing and curation operation rather than a feature. - Any clinical claim. This is not a medical device and cannot be used to manage a diagnosed condition. - Coaching, macronutrient periodisation and training integration, which the hosted product builds on top of the same expenditure model. ### README must contain - That the safety floor and rate cap are enforced in the computation and cannot be crossed - That expenditure is estimated from your logging, so under-reporting produces a low estimate and a lower target - That daily weight is mostly water and only the trend is used - That no expenditure figure is shown before a minimum window of data - That there are no streaks, scores or food grades anywhere in the interface ===== ARCHITECTURE.md ===== # Architecture · MacroFactor ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a local application and SQLite | food and weight logs are health data and belong on your own machine | | Trend | An exponential moving average over daily weights | a single day's weight is mostly water and cannot be used for anything | | Expenditure | Intake minus the energy implied by the trend change, over a rolling window | this is the whole idea, and it needs weeks of data before it means anything | | Food data | A local food database you import, with your own entries on top | the database is the boring part and the part that determines whether anyone uses it | | Safety | Floors and rate caps enforced before any recommendation is shown | the algorithm must not be able to produce an unsafe target | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Trend | Smoothing the daily weight series. | Any smoother. Never using a raw daily value is the requirement. | | Logging | Fast entry and completeness marking. | Any implementation, provided incomplete days are never counted as low intake. | | Expenditure | Deriving burn from intake and trend change. | Any window method, provided it refuses below the minimum window. | | Safety | Floor and rate cap. | Not swappable and not configurable upward at runtime. It is the constraint the rest sits inside. | | Adaptation | Bounded weekly target updates. | Any cadence, provided it pauses on degraded logging rather than adapting to it. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Weights and food logs. Health data, so treat backups accordingly. - `FLOOR_KCAL` · required · The absolute minimum target. Enforced in the safety module, not the interface. - `MAX_RATE_KG_PER_WEEK` · required · The fastest change the tool will plan for. Goals beyond it are refused. - `MIN_WINDOW_DAYS` · required · Below this no expenditure figure is shown at all, because it would be noise. - `TREND_HALF_LIFE_DAYS` · required · How fast the trend responds. Shorter chases water, longer lags real change. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · MacroFactor product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: TypeScript with a local application and SQLite, An exponential moving average over daily weights, Intake minus the energy implied by the trend change, over a rolling window, A local food database you import, with your own entries on top, Floors and rate caps enforced before any recommendation is shown. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. - A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. ===== MILESTONES.md ===== # Delivery milestones · MacroFactor Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · A weight trend that is not the scale reading You can see the signal in a noisy daily weight series. ### Steps 1. Smooth with an exponential moving average A pure function over the series, unit tested with a synthetic step change. Files: `src/trend.ts`, `src/trend.test.ts` 2. Handle missing days explicitly People skip days. The smoothing must not assume a dense series. Files: `src/trend.ts` 3. Show raw against trend So the user sees how little a single day means. This is educational, not decorative. Files: `src/chart.ts` ### Done when - [ ] A trend smooths and lags the raw series - [ ] A week of missing days does not break smoothing - [ ] Raw and trend display together - [ ] One outlier does not dominate the trend ### Watch out - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. ## M2 · Logging food without it being a chore A normal day's food is logged in a couple of minutes. ### Steps 1. Import a food database locally Search must be fast offline. This is the unglamorous part that decides adoption. Files: `src/foods.ts` 2. Saved meals and repeat yesterday Most people eat the same things. Exploit that or the logging stops. Files: `src/quick.ts` 3. Mark incomplete days Never treat a half-logged day as a low intake day. It poisons every later number. Files: `src/entries.ts` ### Done when - [ ] A realistic day logs in under two minutes - [ ] Saved meals and repeat-yesterday work - [ ] Custom foods can be added inline - [ ] An incomplete day is marked, not counted as low ### Watch out - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. ## M3 · Expenditure from your own data The tool tells you what you actually burn, with an honest confidence. ### Steps 1. Compute over a rolling window Average intake minus energy implied by trend change. Excluding incomplete days. Files: `src/expenditure.ts` 2. Refuse below a minimum window No number is better than a number that is wrong by a thousand. Files: `src/expenditure.ts` 3. Validate against a synthetic dataset Known expenditure in, recovered expenditure out, within a stated tolerance. Files: `src/expenditure.test.ts` ### Done when - [ ] Expenditure computes from complete days over a window - [ ] Too little data shows no number and explains why - [ ] Confidence reflects window length and completeness - [ ] A synthetic dataset recovers its known expenditure ### Watch out - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. ## M4 · Targets with a floor the maths cannot cross A recommended intake exists and it is never unsafe. ### Steps 1. Derive the target from measured expenditure Not from a formula. That is the entire premise of the tool. Files: `src/target.ts` 2. Enforce floor and rate cap before display In the computation, not the interface. Property test that nothing crosses them. Files: `src/safety.ts`, `src/safety.test.ts` 3. Refuse and explain rather than clamp A silent clamp looks like agreement and the user tries again. Files: `src/goals.ts` ### Done when - [ ] A target derives from measured expenditure - [ ] A goal below the floor is refused with a reason - [ ] A rate above the cap is refused - [ ] Property testing finds no input that crosses the floor ### Watch out - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. ## M5 · Adapting without chasing noise The target updates weekly as your expenditure changes, and does not thrash. ### Steps 1. Update on a cadence with a step limit Weekly, bounded. Continuous adaptation chases water weight. Files: `src/adapt.ts` 2. Pause on degraded logging The spiral this prevents is the most important behaviour in the tool. Files: `src/adapt.ts` 3. Explain every change Previous, new, and why. An unexplained change in a health target erodes trust. Files: `src/adapt.ts` ### Done when - [ ] The target updates weekly with a bounded step - [ ] Degraded logging pauses adaptation - [ ] Every update shows its reason - [ ] An implausible expenditure change is flagged ### Watch out - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ## M6 · Review, export and deleting it all (production only) You can see the long view, take your data with you, and remove it completely. ### Steps 1. Report without grading No streaks, no percentages framed as compliance, no good and bad foods. Files: `src/review.ts` 2. Export raw and derived Round trip tested. Health data should never be trapped in one application. Files: `src/export.ts`, `tests/roundtrip.test.ts` 3. Delete completely Derived tables and backups included. Health data deletion should be total. Files: `src/deletion.ts` ### Done when - [ ] The review contains no score, grade or streak - [ ] Export round trips raw and derived data - [ ] Deletion removes derived tables and backups - [ ] The long view is readable over months ### Watch out - A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. ===== OPERATIONS.md ===== # Operations · MacroFactor ## Backup Weights and logs are health data and years of them are irreplaceable. Encrypted backups, and deletion that reaches the backups too, since a deleted log restored from backup is data the user asked you to destroy. ## Restore Restore and confirm derived tables are recomputed rather than restored stale, since an expenditure figure carried over from before a gap in logging is a wrong number presented confidently. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For a personal tool the thing to watch is logging completeness, because every downstream number degrades with it and the degradation is invisible in the output. ## Incident checklist A target that has fallen steadily over weeks is the failure mode to look for, since it usually means adaptation is following under-reporting rather than real change. The pause logic is the control and belongs in every release check. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Property testing is confirmed to find no input crossing the floor - [ ] An expenditure figure is confirmed absent below the minimum window - [ ] Degraded logging is confirmed to pause adaptation - [ ] The review surface is confirmed to contain no score, grade or streak - [ ] The synthetic dataset is confirmed to recover its known expenditure ## Launch constraint Do not market omitted MacroFactor capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Weights and food logs. Health data, so treat backups accordingly. DB_PATH=~/.local/share/macrofactor/data.db # Required. The absolute minimum target. Enforced in the safety module, not the interface. FLOOR_KCAL=1500 # Required. The fastest change the tool will plan for. Goals beyond it are refused. MAX_RATE_KG_PER_WEEK=0.75 # Required. Below this no expenditure figure is shown at all, because it would be noise. MIN_WINDOW_DAYS=21 # Required. How fast the trend responds. Shorter chases water, longer lags real change. TREND_HALF_LIFE_DAYS=10
# MacroFactor · indie build A nutrition tracker whose distinguishing feature is that it never uses a formula for your energy expenditure. It derives expenditure from the relationship between what you logged and how your weight trend actually moved, which means the interesting work is separating signal from noise in daily weight and being honest about the error you cannot remove. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a local application and SQLite | food and weight logs are health data and belong on your own machine | | Trend | An exponential moving average over daily weights | a single day's weight is mostly water and cannot be used for anything | | Expenditure | Intake minus the energy implied by the trend change, over a rolling window | this is the whole idea, and it needs weeks of data before it means anything | | Food data | A local food database you import, with your own entries on top | the database is the boring part and the part that determines whether anyone uses it | | Safety | Floors and rate caps enforced before any recommendation is shown | the algorithm must not be able to produce an unsafe target | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **Your floor, your rate cap and who this is not for** · free - Why: These are enforced in code before anything is displayed, and deciding them after the algorithm exists means deciding them to fit the algorithm. - Get it: Write down an absolute minimum daily intake, a maximum weekly rate of change, and a plain statement that this tool is not appropriate for anyone with a history of disordered eating. - Verify: All three are written down and the first two appear as constants in the safety module - [ ] **A food database you may use** · free for public datasets; commercial databases are licensed - Why: Search over a local database is what makes logging fast enough to keep happening, and licence terms differ sharply between sources. - Get it: A publicly available national nutrient database, checking the licence covers your use. Import it locally rather than querying a service. - Verify: Searching returns results offline in under a hundred milliseconds - [ ] **A scale and eight weeks of your own data** · free if you own a scale, and it takes eight weeks - Why: Expenditure estimation cannot be evaluated on synthetic data alone, and the minimum useful window is measured in weeks rather than days. - Get it: Weigh daily at a consistent time and log intake honestly, including the days you would rather not. - Verify: Eight weeks of weights and mostly complete intake logs exist ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. - food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise - wearable hardware data - coach network - clinical validation - large content library and social network If one of those is essential to you, that is the reason to keep paying for MacroFactor, and the README should say so rather than pretend.
# Build brief · MacroFactor The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a nutrition tracker that works out your actual energy expenditure from your own weight and intake data to replace MacroFactor. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This tool asks someone to weigh themselves daily and log everything they eat, and then it tells them a number to eat. That combination is used by people with eating disorders and it makes them worse. Build the safeguards before the algorithm. Concretely: a floor below which the target will not go regardless of what the maths says, a cap on the rate of loss the tool will plan for, no streak counters or guilt language for a missed day, and no scoring of individual foods as good or bad. If a user sets a goal below the floor the tool refuses and says why. The other honest limit is that this estimates expenditure from logged intake, so it is only as accurate as the logging. Someone under-reporting will be told their expenditure is low and given a lower target, which is a loop that ends badly. Say this in the interface, not only in the README. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a local application and SQLite · food and weight logs are health data and belong on your own machine - Trend: An exponential moving average over daily weights · a single day's weight is mostly water and cannot be used for anything - Expenditure: Intake minus the energy implied by the trend change, over a rolling window · this is the whole idea, and it needs weeks of data before it means anything - Food data: A local food database you import, with your own entries on top · the database is the boring part and the part that determines whether anyone uses it - Safety: Floors and rate caps enforced before any recommendation is shown · the algorithm must not be able to produce an unsafe target ### Data model (create this before Phase 1) - weights(date, kg, entered_at) · one per day, and days are allowed to be missing - trend(date, kg) · derived, never entered, an exponential moving average over the raw series - foods(id, name, kcal_per_100g, protein_g, carb_g, fat_g, source) · source is imported or user - entries(id, date, food_id, grams, meal, logged_at) · the log, kept even when a day is incomplete - expenditure(date, kcal, window_days, confidence) · derived, with the window it was computed over - goals(id, started_at, direction, rate_kg_per_week, floor_kcal, ended_at) · the rate cap and floor live here - Expenditure is a derived table with the window recorded, because a number computed over eight days and one computed over eight weeks are not the same claim. ### Phase 1 · A weight trend that is not the scale reading Build: Daily weight entry and an exponential moving average trend over it. Handle missing days without breaking the smoothing. Show raw and trend together so the difference is visible. Trap: Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. Done when: A daily series produces a trend that visibly lags and smooths the raw values. Missing days do not create a discontinuity, tested with a week of gaps. Raw and trend display together. A single large outlier moves the trend slightly rather than dominating it. Do not build yet: No intake, expenditure or goals. ### Phase 2 · Logging food without it being a chore Build: A local food database with search, custom foods, saved meals and a repeat-yesterday action. Log by portion as well as by weight. Measure how long a realistic day takes and treat it as a requirement. Trap: A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. Done when: A realistic day logs in under two minutes, timed by hand. Saved meals and repeat-yesterday both work. Custom foods can be created in the flow rather than in a separate screen. A partially logged day is marked as incomplete rather than counted as a low intake day. Do not build yet: No expenditure calculation yet. ### Phase 3 · Expenditure from your own data Build: Compute expenditure over a rolling window as average intake minus the energy implied by the trend change. Exclude incomplete days. Report a confidence that reflects the window length and the amount of logged data, and refuse to show a number below a minimum window. Trap: Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. Done when: Expenditure computes over a rolling window from complete days only. Fewer days than the minimum shows no number and says why, tested by entering one week of data. The confidence reflects window length and completeness. A synthetic dataset with a known expenditure recovers it within a stated tolerance, asserted in a test. Do not build yet: No targets or goals yet. ### Phase 4 · Targets with a floor the maths cannot cross Build: Derive a daily target from expenditure and a chosen rate of change. Enforce an absolute floor and a maximum rate of loss before the number is shown. Refuse goals that would require crossing either, and explain rather than silently clamping. Trap: Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. Done when: A target derives from expenditure and the chosen rate. A goal below the floor is refused with an explanation, tested directly. A rate above the cap is refused. The floor cannot be crossed by any combination of inputs, verified by property testing across a range of values. Do not build yet: No adaptation or review yet. ### Phase 5 · Adapting without chasing noise Build: Update the target on a fixed cadence rather than continuously, limit how far it can move in one update, and show what changed and why. Detect the case where logging quality has dropped and pause adaptation instead of adapting to bad data. Trap: Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. Done when: The target updates on a weekly cadence with a limited step size. A drop in logging completeness pauses adaptation and says so, tested by degrading the log. Every update shows the previous value, the new value and the reason. An implausible week-over-week expenditure change is flagged rather than applied. Do not build yet: Review and export come last. ### Phase 6 · Review, export and deleting it all Build: A review showing trend, expenditure and adherence over months without scoring the user. Full export of weights, entries and derived series. Complete deletion including derived tables and backups. Trap: A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. Done when: The review shows trend, expenditure and intake over months with no score, grade or streak anywhere in it, verified by reading the whole surface. Export contains raw and derived data and re-imports identically. Deletion removes entries, weights, derived tables and any local backup. Do not build yet: Nothing after this. ### Out of scope (and why) - Barcode scanning and photo food recognition, which is most of what makes a commercial tracker fast to use. - A comprehensive maintained food database, which is a data licensing and curation operation rather than a feature. - Any clinical claim. This is not a medical device and cannot be used to manage a diagnosed condition. - Coaching, macronutrient periodisation and training integration, which the hosted product builds on top of the same expenditure model. ### README must contain - That the safety floor and rate cap are enforced in the computation and cannot be crossed - That expenditure is estimated from your logging, so under-reporting produces a low estimate and a lower target - That daily weight is mostly water and only the trend is used - That no expenditure figure is shown before a minimum window of data - That there are no streaks, scores or food grades anywhere in the interface
# Agent instructions · MacroFactor indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with a local application and SQLite, An exponential moving average over daily weights, Intake minus the energy implied by the trend change, over a rolling window, A local food database you import, with your own entries on top, Floors and rate caps enforced before any recommendation is shown. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt.
# Build plan · MacroFactor A nutrition tracker whose distinguishing feature is that it never uses a formula for your energy expenditure. It derives expenditure from the relationship between what you logged and how your weight trend actually moved, which means the interesting work is separating signal from noise in daily weight and being honest about the error you cannot remove. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · A weight trend that is not the scale reading You can see the signal in a noisy daily weight series. ### Steps 1. Smooth with an exponential moving average A pure function over the series, unit tested with a synthetic step change. Files: `src/trend.ts`, `src/trend.test.ts` 2. Handle missing days explicitly People skip days. The smoothing must not assume a dense series. Files: `src/trend.ts` 3. Show raw against trend So the user sees how little a single day means. This is educational, not decorative. Files: `src/chart.ts` ### Done when - [ ] A trend smooths and lags the raw series - [ ] A week of missing days does not break smoothing - [ ] Raw and trend display together - [ ] One outlier does not dominate the trend ### Watch out - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. ## Phase 2 · Logging food without it being a chore A normal day's food is logged in a couple of minutes. ### Steps 1. Import a food database locally Search must be fast offline. This is the unglamorous part that decides adoption. Files: `src/foods.ts` 2. Saved meals and repeat yesterday Most people eat the same things. Exploit that or the logging stops. Files: `src/quick.ts` 3. Mark incomplete days Never treat a half-logged day as a low intake day. It poisons every later number. Files: `src/entries.ts` ### Done when - [ ] A realistic day logs in under two minutes - [ ] Saved meals and repeat-yesterday work - [ ] Custom foods can be added inline - [ ] An incomplete day is marked, not counted as low ### Watch out - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. ## Phase 3 · Expenditure from your own data The tool tells you what you actually burn, with an honest confidence. ### Steps 1. Compute over a rolling window Average intake minus energy implied by trend change. Excluding incomplete days. Files: `src/expenditure.ts` 2. Refuse below a minimum window No number is better than a number that is wrong by a thousand. Files: `src/expenditure.ts` 3. Validate against a synthetic dataset Known expenditure in, recovered expenditure out, within a stated tolerance. Files: `src/expenditure.test.ts` ### Done when - [ ] Expenditure computes from complete days over a window - [ ] Too little data shows no number and explains why - [ ] Confidence reflects window length and completeness - [ ] A synthetic dataset recovers its known expenditure ### Watch out - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. ## Phase 4 · Targets with a floor the maths cannot cross A recommended intake exists and it is never unsafe. ### Steps 1. Derive the target from measured expenditure Not from a formula. That is the entire premise of the tool. Files: `src/target.ts` 2. Enforce floor and rate cap before display In the computation, not the interface. Property test that nothing crosses them. Files: `src/safety.ts`, `src/safety.test.ts` 3. Refuse and explain rather than clamp A silent clamp looks like agreement and the user tries again. Files: `src/goals.ts` ### Done when - [ ] A target derives from measured expenditure - [ ] A goal below the floor is refused with a reason - [ ] A rate above the cap is refused - [ ] Property testing finds no input that crosses the floor ### Watch out - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. ## Phase 5 · Adapting without chasing noise The target updates weekly as your expenditure changes, and does not thrash. ### Steps 1. Update on a cadence with a step limit Weekly, bounded. Continuous adaptation chases water weight. Files: `src/adapt.ts` 2. Pause on degraded logging The spiral this prevents is the most important behaviour in the tool. Files: `src/adapt.ts` 3. Explain every change Previous, new, and why. An unexplained change in a health target erodes trust. Files: `src/adapt.ts` ### Done when - [ ] The target updates weekly with a bounded step - [ ] Degraded logging pauses adaptation - [ ] Every update shows its reason - [ ] An implausible expenditure change is flagged ### Watch out - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ## Not in this build - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. ## After v1, if you want it - Protein and fibre targets alongside energy, which most people need more than precision on calories - Recomposition handling, where weight barely moves and the expenditure model needs care - A diet break mode that pauses goals without pausing measurement
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Weights and food logs. Health data, so treat backups accordingly. DB_PATH=~/.local/share/macrofactor/data.db # Required. The absolute minimum target. Enforced in the safety module, not the interface. FLOOR_KCAL=1500 # Required. The fastest change the tool will plan for. Goals beyond it are refused. MAX_RATE_KG_PER_WEEK=0.75 # Required. Below this no expenditure figure is shown at all, because it would be noise. MIN_WINDOW_DAYS=21 # Required. How fast the trend responds. Shorter chases water, longer lags real change. TREND_HALF_LIFE_DAYS=10
# MacroFactor · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For MacroFactor, record user-chosen nutrition information and show neutral trends without aggressive targets. The hard boundary is food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise, plus hardware data, content, coaching, and trust. ## Product outcome A tracker that measures your energy expenditure instead of guessing it from a formula, refuses to show a number it cannot support with data, and cannot be made to recommend an unsafe target. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - iOS or Android device - optional HealthKit or Health Connect permission - local storage ## Explicit non-goals for v1 - Barcode scanning and photo food recognition. - A comprehensive maintained food database. - Any clinical or medical claim. - Coaching, periodisation and training integration. - food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise - wearable hardware data - coach network - clinical validation - large content library and social network ## Success criteria - Property testing is confirmed to find no input crossing the floor - An expenditure figure is confirmed absent below the minimum window - Degraded logging is confirmed to pause adaptation - The review surface is confirmed to contain no score, grade or streak - The synthetic dataset is confirmed to recover its known expenditure
# Build brief · MacroFactor The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a nutrition tracker that works out your actual energy expenditure from your own weight and intake data to replace MacroFactor. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This tool asks someone to weigh themselves daily and log everything they eat, and then it tells them a number to eat. That combination is used by people with eating disorders and it makes them worse. Build the safeguards before the algorithm. Concretely: a floor below which the target will not go regardless of what the maths says, a cap on the rate of loss the tool will plan for, no streak counters or guilt language for a missed day, and no scoring of individual foods as good or bad. If a user sets a goal below the floor the tool refuses and says why. The other honest limit is that this estimates expenditure from logged intake, so it is only as accurate as the logging. Someone under-reporting will be told their expenditure is low and given a lower target, which is a loop that ends badly. Say this in the interface, not only in the README. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a local application and SQLite · food and weight logs are health data and belong on your own machine - Trend: An exponential moving average over daily weights · a single day's weight is mostly water and cannot be used for anything - Expenditure: Intake minus the energy implied by the trend change, over a rolling window · this is the whole idea, and it needs weeks of data before it means anything - Food data: A local food database you import, with your own entries on top · the database is the boring part and the part that determines whether anyone uses it - Safety: Floors and rate caps enforced before any recommendation is shown · the algorithm must not be able to produce an unsafe target ### Data model (create this before Phase 1) - weights(date, kg, entered_at) · one per day, and days are allowed to be missing - trend(date, kg) · derived, never entered, an exponential moving average over the raw series - foods(id, name, kcal_per_100g, protein_g, carb_g, fat_g, source) · source is imported or user - entries(id, date, food_id, grams, meal, logged_at) · the log, kept even when a day is incomplete - expenditure(date, kcal, window_days, confidence) · derived, with the window it was computed over - goals(id, started_at, direction, rate_kg_per_week, floor_kcal, ended_at) · the rate cap and floor live here - Expenditure is a derived table with the window recorded, because a number computed over eight days and one computed over eight weeks are not the same claim. ### Phase 1 · A weight trend that is not the scale reading Build: Daily weight entry and an exponential moving average trend over it. Handle missing days without breaking the smoothing. Show raw and trend together so the difference is visible. Trap: Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. Done when: A daily series produces a trend that visibly lags and smooths the raw values. Missing days do not create a discontinuity, tested with a week of gaps. Raw and trend display together. A single large outlier moves the trend slightly rather than dominating it. Do not build yet: No intake, expenditure or goals. ### Phase 2 · Logging food without it being a chore Build: A local food database with search, custom foods, saved meals and a repeat-yesterday action. Log by portion as well as by weight. Measure how long a realistic day takes and treat it as a requirement. Trap: A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. Done when: A realistic day logs in under two minutes, timed by hand. Saved meals and repeat-yesterday both work. Custom foods can be created in the flow rather than in a separate screen. A partially logged day is marked as incomplete rather than counted as a low intake day. Do not build yet: No expenditure calculation yet. ### Phase 3 · Expenditure from your own data Build: Compute expenditure over a rolling window as average intake minus the energy implied by the trend change. Exclude incomplete days. Report a confidence that reflects the window length and the amount of logged data, and refuse to show a number below a minimum window. Trap: Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. Done when: Expenditure computes over a rolling window from complete days only. Fewer days than the minimum shows no number and says why, tested by entering one week of data. The confidence reflects window length and completeness. A synthetic dataset with a known expenditure recovers it within a stated tolerance, asserted in a test. Do not build yet: No targets or goals yet. ### Phase 4 · Targets with a floor the maths cannot cross Build: Derive a daily target from expenditure and a chosen rate of change. Enforce an absolute floor and a maximum rate of loss before the number is shown. Refuse goals that would require crossing either, and explain rather than silently clamping. Trap: Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. Done when: A target derives from expenditure and the chosen rate. A goal below the floor is refused with an explanation, tested directly. A rate above the cap is refused. The floor cannot be crossed by any combination of inputs, verified by property testing across a range of values. Do not build yet: No adaptation or review yet. ### Phase 5 · Adapting without chasing noise Build: Update the target on a fixed cadence rather than continuously, limit how far it can move in one update, and show what changed and why. Detect the case where logging quality has dropped and pause adaptation instead of adapting to bad data. Trap: Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. Done when: The target updates on a weekly cadence with a limited step size. A drop in logging completeness pauses adaptation and says so, tested by degrading the log. Every update shows the previous value, the new value and the reason. An implausible week-over-week expenditure change is flagged rather than applied. Do not build yet: Review and export come last. ### Phase 6 · Review, export and deleting it all Build: A review showing trend, expenditure and adherence over months without scoring the user. Full export of weights, entries and derived series. Complete deletion including derived tables and backups. Trap: A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak. Done when: The review shows trend, expenditure and intake over months with no score, grade or streak anywhere in it, verified by reading the whole surface. Export contains raw and derived data and re-imports identically. Deletion removes entries, weights, derived tables and any local backup. Do not build yet: Nothing after this. ### Out of scope (and why) - Barcode scanning and photo food recognition, which is most of what makes a commercial tracker fast to use. - A comprehensive maintained food database, which is a data licensing and curation operation rather than a feature. - Any clinical claim. This is not a medical device and cannot be used to manage a diagnosed condition. - Coaching, macronutrient periodisation and training integration, which the hosted product builds on top of the same expenditure model. ### README must contain - That the safety floor and rate cap are enforced in the computation and cannot be crossed - That expenditure is estimated from your logging, so under-reporting produces a low estimate and a lower target - That daily weight is mostly water and only the trend is used - That no expenditure figure is shown before a minimum window of data - That there are no streaks, scores or food grades anywhere in the interface
# Architecture · MacroFactor ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a local application and SQLite | food and weight logs are health data and belong on your own machine | | Trend | An exponential moving average over daily weights | a single day's weight is mostly water and cannot be used for anything | | Expenditure | Intake minus the energy implied by the trend change, over a rolling window | this is the whole idea, and it needs weeks of data before it means anything | | Food data | A local food database you import, with your own entries on top | the database is the boring part and the part that determines whether anyone uses it | | Safety | Floors and rate caps enforced before any recommendation is shown | the algorithm must not be able to produce an unsafe target | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Trend | Smoothing the daily weight series. | Any smoother. Never using a raw daily value is the requirement. | | Logging | Fast entry and completeness marking. | Any implementation, provided incomplete days are never counted as low intake. | | Expenditure | Deriving burn from intake and trend change. | Any window method, provided it refuses below the minimum window. | | Safety | Floor and rate cap. | Not swappable and not configurable upward at runtime. It is the constraint the rest sits inside. | | Adaptation | Bounded weekly target updates. | Any cadence, provided it pauses on degraded logging rather than adapting to it. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Weights and food logs. Health data, so treat backups accordingly. - `FLOOR_KCAL` · required · The absolute minimum target. Enforced in the safety module, not the interface. - `MAX_RATE_KG_PER_WEEK` · required · The fastest change the tool will plan for. Goals beyond it are refused. - `MIN_WINDOW_DAYS` · required · Below this no expenditure figure is shown at all, because it would be noise. - `TREND_HALF_LIFE_DAYS` · required · How fast the trend responds. Shorter chases water, longer lags real change. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · MacroFactor product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: TypeScript with a local application and SQLite, An exponential moving average over daily weights, Intake minus the energy implied by the trend change, over a rolling window, A local food database you import, with your own entries on top, Floors and rate caps enforced before any recommendation is shown. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. - A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak.
# Delivery milestones · MacroFactor Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · A weight trend that is not the scale reading You can see the signal in a noisy daily weight series. ### Steps 1. Smooth with an exponential moving average A pure function over the series, unit tested with a synthetic step change. Files: `src/trend.ts`, `src/trend.test.ts` 2. Handle missing days explicitly People skip days. The smoothing must not assume a dense series. Files: `src/trend.ts` 3. Show raw against trend So the user sees how little a single day means. This is educational, not decorative. Files: `src/chart.ts` ### Done when - [ ] A trend smooths and lags the raw series - [ ] A week of missing days does not break smoothing - [ ] Raw and trend display together - [ ] One outlier does not dominate the trend ### Watch out - Treating each day's weight as a measurement of body mass is wrong by several kilograms: a gram of stored glycogen holds around three grams of water with it, and sodium, food in transit and hormonal cycles move the number far more than fat does over a week. Smooth it, and never show a raw day-to-day change as though it means something. ## M2 · Logging food without it being a chore A normal day's food is logged in a couple of minutes. ### Steps 1. Import a food database locally Search must be fast offline. This is the unglamorous part that decides adoption. Files: `src/foods.ts` 2. Saved meals and repeat yesterday Most people eat the same things. Exploit that or the logging stops. Files: `src/quick.ts` 3. Mark incomplete days Never treat a half-logged day as a low intake day. It poisons every later number. Files: `src/entries.ts` ### Done when - [ ] A realistic day logs in under two minutes - [ ] Saved meals and repeat-yesterday work - [ ] Custom foods can be added inline - [ ] An incomplete day is marked, not counted as low ### Watch out - A tracker where logging a normal day takes ten minutes is abandoned within a fortnight, and then every downstream number is computed from a partial log, which is worse than having no numbers. Saved meals and repeating a previous day are not conveniences here, they are what makes the data exist at all. ## M3 · Expenditure from your own data The tool tells you what you actually burn, with an honest confidence. ### Steps 1. Compute over a rolling window Average intake minus energy implied by trend change. Excluding incomplete days. Files: `src/expenditure.ts` 2. Refuse below a minimum window No number is better than a number that is wrong by a thousand. Files: `src/expenditure.ts` 3. Validate against a synthetic dataset Known expenditure in, recovered expenditure out, within a stated tolerance. Files: `src/expenditure.test.ts` ### Done when - [ ] Expenditure computes from complete days over a window - [ ] Too little data shows no number and explains why - [ ] Confidence reflects window length and completeness - [ ] A synthetic dataset recovers its known expenditure ### Watch out - Showing an expenditure figure after five days is worse than showing nothing, because the trend change over that period is almost entirely water and the resulting number can be wrong by a thousand calories. Require a minimum window and widen it when logging has been patchy, rather than always producing an answer. ## M4 · Targets with a floor the maths cannot cross A recommended intake exists and it is never unsafe. ### Steps 1. Derive the target from measured expenditure Not from a formula. That is the entire premise of the tool. Files: `src/target.ts` 2. Enforce floor and rate cap before display In the computation, not the interface. Property test that nothing crosses them. Files: `src/safety.ts`, `src/safety.test.ts` 3. Refuse and explain rather than clamp A silent clamp looks like agreement and the user tries again. Files: `src/goals.ts` ### Done when - [ ] A target derives from measured expenditure - [ ] A goal below the floor is refused with a reason - [ ] A rate above the cap is refused - [ ] Property testing finds no input that crosses the floor ### Watch out - Clamping silently to the floor makes the tool appear to have agreed to an unsafe goal, and the user sets it again in a month. Refuse the goal, say which limit it crossed, and offer the nearest acceptable one, so the constraint is visible rather than hidden inside the arithmetic. ## M5 · Adapting without chasing noise The target updates weekly as your expenditure changes, and does not thrash. ### Steps 1. Update on a cadence with a step limit Weekly, bounded. Continuous adaptation chases water weight. Files: `src/adapt.ts` 2. Pause on degraded logging The spiral this prevents is the most important behaviour in the tool. Files: `src/adapt.ts` 3. Explain every change Previous, new, and why. An unexplained change in a health target erodes trust. Files: `src/adapt.ts` ### Done when - [ ] The target updates weekly with a bounded step - [ ] Degraded logging pauses adaptation - [ ] Every update shows its reason - [ ] An implausible expenditure change is flagged ### Watch out - Adapting to a falling apparent expenditure caused by under-reporting produces a lower target, which increases the pressure that caused the under-reporting, and the target spirals downward on data that was never real. Detect a sudden drop in logging completeness or an implausible expenditure change and pause rather than adapt. ## M6 · Review, export and deleting it all (production only) You can see the long view, take your data with you, and remove it completely. ### Steps 1. Report without grading No streaks, no percentages framed as compliance, no good and bad foods. Files: `src/review.ts` 2. Export raw and derived Round trip tested. Health data should never be trapped in one application. Files: `src/export.ts`, `tests/roundtrip.test.ts` 3. Delete completely Derived tables and backups included. Health data deletion should be total. Files: `src/deletion.ts` ### Done when - [ ] The review contains no score, grade or streak - [ ] Export round trips raw and derived data - [ ] Deletion removes derived tables and backups - [ ] The long view is readable over months ### Watch out - A review that reports adherence as a percentage with a streak beside it turns a measurement tool into a compliance scorecard, which is precisely the failure mode this whole build is trying to avoid. Report what happened without grading it, and never display a broken streak.
# Operations · MacroFactor ## Backup Weights and logs are health data and years of them are irreplaceable. Encrypted backups, and deletion that reaches the backups too, since a deleted log restored from backup is data the user asked you to destroy. ## Restore Restore and confirm derived tables are recomputed rather than restored stale, since an expenditure figure carried over from before a gap in logging is a wrong number presented confidently. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For a personal tool the thing to watch is logging completeness, because every downstream number degrades with it and the degradation is invisible in the output. ## Incident checklist A target that has fallen steadily over weeks is the failure mode to look for, since it usually means adaptation is following under-reporting rather than real change. The pause logic is the control and belongs in every release check. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Property testing is confirmed to find no input crossing the floor - [ ] An expenditure figure is confirmed absent below the minimum window - [ ] Degraded logging is confirmed to pause adaptation - [ ] The review surface is confirmed to contain no score, grade or streak - [ ] The synthetic dataset is confirmed to recover its known expenditure ## Launch constraint Do not market omitted MacroFactor capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Weights and food logs. Health data, so treat backups accordingly. DB_PATH=~/.local/share/macrofactor/data.db # Required. The absolute minimum target. Enforced in the safety module, not the interface. FLOOR_KCAL=1500 # Required. The fastest change the tool will plan for. Goals beyond it are refused. MAX_RATE_KG_PER_WEEK=0.75 # Required. Below this no expenditure figure is shown at all, because it would be noise. MIN_WINDOW_DAYS=21 # Required. How fast the trend responds. Shorter chases water, longer lags real change. TREND_HALF_LIFE_DAYS=10
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for MacroFactor because people pay for polished mobile capture, trusted content, hardware integration, and coaching rather than the basic logbook. The recurring cost buys sensor permissions, data quality, notifications, safety language, accessibility, sync, privacy, content review, and support, not just the visible interface.
xfood database, adaptive coaching models, mobile capture, and ongoing nutrition expertise
xwearable hardware data
xcoach network
xclinical validation
xlarge content library and social network
Don't feel like building it? These folks already made it free.
all 4 free alternatives to MacroFactor →· no votes, no pay-to-list · just what's real
MacroFactor pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| macrofactor nutrition, monthly | $11.99/user | — | All nutrition logging, expenditure, coaching, trend, and analytics features |
| macrofactor nutrition, 6 months | custom | — | All nutrition features for a 6-month term |
| macrofactor nutrition, annual | — | $6/user | All nutrition features for 12 months |
| macrofactor nutrition + workouts bundle | — | $7.50/user | Both MacroFactor Nutrition and MacroFactor Workouts after the first bundle year |
free tierno permanent free tier; a trial may be offered, but the current official public pages did not publish a numeric trial duration
billingmonthly, 6-month, and annual Nutrition subscriptions; Workouts is separate, with an annual bundle path
hidden costsMacroFactor Workouts is a separate subscription at the same listed price points unless bundled. The $89.99 combined annual price applies after the first bundle year; taxes/storefront pricing can vary.
verified 2026-08-14 · source ↗
Vibecode MacroFactor
Kinda. The core of MacroFactor is buildable in a weekend with the prompt on this page, but there are real gaps: food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise, wearable hardware data. Read the honest list above before committing.
How much does MacroFactor cost?
MacroFactor costs about $11.99/month (MacroFactor, checked 2026-07-31), which is $143.88 per year.
What do I lose by replacing MacroFactor?
Honestly: food database, adaptive coaching models, mobile capture, and ongoing nutrition expertise; wearable hardware data; coach network; clinical validation; large content library and social network. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to MacroFactor?
Yes: wger (Meals, workouts and trend charts in one open-source bunker; polish is not the main feature.) OpenNutriTracker (Calories, macros and micronutrients stay on your phone; the app declines to become your diet coach.) Waistline (A plain Android calorie diary that keeps its data local and its opinions to itself.) All 4 curated free alternatives are at vibecodeit.com/macrofactor/alternatives. The prompt is for when you want it exactly your way.