# PROJECT.md — Strength-training coach ## What this is A personal, science-based strength-training coach I'm building solo for the Gradio/Hugging Face "Build Small Hackathon" (Backyard AI track). It's a Gradio app deployed as a Hugging Face Space. **One-line idea:** a pocket strength coach that reads my daily check-in, holds my long-term plan, and reshapes each session to the time, energy, sleep, and constraints I actually have, so I keep making progress whether I have 45 minutes or 90. ## The user Me. One real user with a real problem: I have a long-term hypertrophy goal, but my available time varies day to day (sometimes 45 min, sometimes 90), and my energy/mood varies too. I want something that takes those into account and still optimizes each session toward continuous progress, instead of a rigid plan I abandon on bad days. This is the Backyard AI track done honestly: I am the genuine person with the genuine need, and the proof it works is that I actually train on it during the build window and accumulate real logged sessions. ## Goal & training context - Focus: **hypertrophy (muscle growth) with continuous, measurable progress.** - Nutrition context for now: training in a **slight caloric surplus or at maintenance** ("lean bulk"). No meal plans in scope. - Out of scope (future, not now): cutting, mini-cuts, recomp, sport-specific peaking. The specific training methodology (how cycles progress, how readiness changes a session, when to deload, volume targets, etc.) comes from my own research and is recorded/decided in `DECISIONS.md` — it is mine to define, not the agent's to invent. ## Architecture (hold to this) Two clearly separated parts: 1. **The engine — deterministic Python, no AI.** Owns all training logic and math: which exercises today, how many sets/reps, target load, and how the session adapts to time available and readiness. This is rules-based and fully testable. This is the intelligence of the product, and it's the part I write deliberately. 2. **The parser/conversational layer — a small LLM for language, not training math.** In the MVP it turns my free-text check-in ("45 min, slept badly, back's tight") into structured inputs the engine understands. Later, it can also narrate the engine's output in a coachy voice ("short on time, so we kept the two main lifts and cut accessories"). The LLM never computes training numbers. **Why this split:** it's the honest "small model used precisely" design, it keeps the app fast and cheap (most interactions are instant deterministic code, not model calls), and it's where the real learning is for me. Persistence: the Hugging Face Space filesystem is **ephemeral** (wiped on restart), so anything that must survive — my profile, training log, settings — is saved to a **Hugging Face Dataset**, not to a local file. ## The daily loop (the spine — this is the MVP) 1. **Open the app** → it loads my plan and history. 2. **Check in** → I write a natural-language note about time, energy, sleep, mood, soreness, and constraints. 3. **Parser extracts structure** → a small model converts the note into engine-safe fields; the UI lets me inspect/edit those fields. 4. **Engine builds today's session** → exercises, sets, reps, target loads, adapted to the structured check-in and history. 5. **(Later) Coach explains** → why today looks the way it does. 6. **I train and log** → the actual weights and reps I hit. 7. **Save** → results persist and inform the next session's loads. Early UI scaffolding may show structured fields, but the MVP includes text-first check-in extraction. The structured fields are there to keep the engine contract visible and editable, not to replace the check-in idea. ## Scope: spine vs. stretch **Spine (MVP — build this first, make it flawless):** - One user. - One training goal, one fixed split I choose, a small fixed exercise list (not a database). - Progression by a single rule (double progression) to start. - Text-first daily check-in extraction into structured time, energy, sleep, mood, and constraint fields. - Daily adaptation from the structured check-in mapped to concrete actions that I explicitly define. - Log what I did; persist it; it informs the next session. A flawless spine alone is a complete, honest, demoable submission. **Stretch (only after the spine is solid, in rough priority order):** 1. Coach-style narration of the deterministic engine output. 2. Progress tab with charts (also serves as proof-of-use for judging). 3. Research-reader: paste a paper → candidate rule → I approve → it changes a recommendation. (The "wow" feature.) 4. Mesocycle/periodization waving, deloads, volume landmarks (from my research). 5. Fine-tune a small model on Modal credits and publish it (learning + a badge). 6. Custom UI polish. None of the stretch items may break the spine. ## Tech & deployment - **Language/UI:** Python + Gradio. `gr.ChatInterface` for the chat; `gr.Blocks` with tabs (Today / Plan / Progress / Research) for the fuller layout — but only Today is the MVP. - **Hosting:** Hugging Face Space (Gradio SDK). Free CPU Basic (2 vCPU, 16 GB RAM) runs the app shell and all engine math fine. - **Model compute (when the LLM comes in):** ZeroGPU via the `@spaces.GPU` decorator — grabs a GPU only while the model runs. Free quota is small (~minutes/day); PRO ($9/mo) raises it to ~25 min/day. ZeroGPU works only with the Gradio SDK. - **Persistence:** a Hugging Face Dataset (free, durable). Persistent Space storage is a paid add-on we are avoiding. - **Heavy one-off jobs (stretch):** ~€250 Modal credits, for a one-time fine-tune or batch job — not as a live inference backend (that would add a cloud dependency and cost the local-first badge). - **Source control:** a GitHub repo is the main home; the HF Space is added as a second git remote and pushed to when deploying. - **Parser model choice (open):** choose a small model for strict check-in JSON extraction. The hackathon cap is ≤32B total parameters; a ≤4B model is attractive if it is reliable enough because it better matches the "small model used precisely" story. ## Hackathon optimization The app should be optimized for the Backyard AI story: one specific real user, one real problem, proof that I actually used it, and a small model doing a precise language job around a deterministic system I can understand. Primary submission assets: - A live Gradio Space. - Real logged sessions from my build week. - A short demo video showing a messy check-in becoming a useful session. - Field notes explaining what I built, what worked, and what I changed from real use. Likely bonus targets, only if they do not threaten the spine: - Field Notes: high value and low risk. - Sharing is Caring: possible if traces are safe and contain no secrets. - Tiny Titan: possible if the parser model is ≤4B and works well. - Custom UI polish: only after the daily loop is solid. - Fine-tuned model or llama.cpp: stretch, not required for MVP. ## Implementation plan ### Phase 0 — Project alignment - Update `PROJECT.md` and `DECISIONS.md` so the MVP includes text-first check-in extraction. - Keep the engine boundary explicit: parser uses AI; engine is deterministic Python. - Keep open items open until I decide them. ### Phase 1 — Real MVP UI shell - Convert the app to `gr.Blocks`. - Add a `Today` tab. - Make the free-text check-in the primary input. - Show editable structured fields for time, energy, sleep, soreness/constraints, and mood/stress. - Keep the session output hardcoded until parser and engine code exist. ### Phase 2 — Core schema - Decide the exact shape of `CheckIn`, `Exercise`, `SessionPlan`, `LoggedSet`, and `SessionLog`. - Implement the approved schema in plain, testable Python. - Add serialization tests. ### Phase 3 — Small model parser - Choose the parser model. - Implement `parse_check_in(text) -> CheckIn` with strict structured output. - Add parser fixtures for short, tired, sore, and high-energy check-ins. - Keep raw text out of the engine. ### Phase 4 — Engine slice - I provide the first explicit training rules. - Encode only those rules as pure deterministic Python. - Add unit tests for each rule. - Produce `build_session(check_in, history, plan) -> SessionPlan`. ### Phase 5 — End-to-end daily loop - Text check-in goes through the parser. - Parsed fields are visible and editable. - The engine builds today's real session. - The UI displays the session clearly enough to train from. ### Phase 6 — Logging - Add performed-set logging. - Save completed sessions through a storage interface. - Start with local JSON behind the interface for fast development. ### Phase 7 — Durable persistence - Add Hugging Face Dataset storage. - Load history on app startup. - Append completed session logs. - Prove data survives restart. ### Phase 8 — Submission readiness - Deploy to Hugging Face Spaces. - Add a small proof-of-use view from real logs. - Write field notes. - Record a demo video. ### Phase 9 — Stretch - Coach narration. - Better parser model or Tiny Titan route. - Progress charts. - Research-reader. - Fine-tuning or llama.cpp only if the spine is already stable. ## Build order (current runnable increments) 1. **Setup:** project + venv + a hello-world Gradio chat running locally; git repo with `.gitignore`. 2. **Make the UI feel alive:** Today tab shows a text-first check-in, editable structured fields, and a hardcoded session preview. 3. **Choose schema:** decide the core data objects before writing engine/storage code. 4. **Add parser:** small model extracts a structured check-in from natural language. 5. **Write the engine** as pure Python, tested on its own. This is the part I drive. 6. **Wire engine → UI:** the Today tab produces real sessions from parsed/editable check-ins. 7. **Logging + persistence:** log actual sets, save through local JSON first, then the Hugging Face Dataset. MVP complete here. 8. **Deploy to the Space.** 9. **Live with it:** train on it on real days, fix friction, and prepare submission assets. 10. **Stretch items**, in priority order, time permitting.