Retrofit per the playbook. Resolve both AGENTS.md TODOs with verified
facts (make-target set; db:seed is live at image-build + local dev),
reconcile the AI-provider count (4 files -> 5 registered providers), and
extract the AI subsystem cheat-sheet into docs/guides/ai-subsystem.md,
lazy-loaded via a .claude/rules symlink with an index line in AGENTS.md.
All AGENTS.md commands verified green (tests 177/177, build, tsc, lint).
Onboarding doc for fresh agent sessions: stack, commands, layout,
conventions, and an Always/Never list of gotchas hit during the AI
overhaul. Current state section tracks the 1.1.0:7 checkpoint.
ROADMAP.md holds the longer-term backlog. CLAUDE.md symlinks AGENTS.md
so Claude Code loads it. Secrets kept out — private registry/file-host
URLs and creds referenced by file location, not value.
The popup HAD an IntersectionObserver-based infinite scroll (since
v1.0.0:6 alongside the main workout-history page), but the observer
was unreliable inside an `absolute`-positioned scroll container with
a small 60px rootMargin. It often didn't fire at all — leaving the
user with a popup that scrolled internally but never fetched more
data even when hundreds of history entries existed server-side.
Fix: replace IntersectionObserver with a plain `scroll` event
listener on the popup. Fires whenever the user scrolls within 300px
of the bottom (matching WorkoutsList's lookahead on the main page).
Also runs once on mount in case the first page doesn't fill the
popup.
Bottom status row now shows "Loading more..." / "Scroll to load
more" / "End of history" so the user has feedback on state.
No schema, no API, no data.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The clock-icon popup in the workout editor was capped at max-h-80
(~320px = ~5 history rows). Users with multi-year history saw older
sessions hidden behind a tiny inner scrollbar. Bumped to 70vh so it
scales with the viewport — ~15+ rows on a normal display, more on a
large monitor.
The IntersectionObserver pagination already loaded more rows on
demand; the old cap just kept them off-screen.
Pure CSS-class change. No schema, no API, no data.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User pointed out their Google AI Studio dropdown shows gemini-3-pro,
gemini-3.1-pro, gemini-3-flash and gemini-2.5-flash — not the longer
preview names I shipped in v1.1.0:4. The menu was missing all the
Flash variants entirely.
Fix:
- Add gemini-3.1-pro (short form, what AI Studio shows)
- Add gemini-3.1-flash + gemini-3.1-flash-lite (the cheapest 3.x)
- Add gemini-3-pro + gemini-3-flash (older tier, still available)
- Pricing entries for all of the above (~$0.50/$3 per M for Flash)
Pure data fix; no schema or behavior changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-feedback-driven release after testing v1.1.0:3. Nine themes:
1. Multi-config persistence
- New AIConfigProfile table (per-user). Save N configs, toggle one
active. Switching providers no longer wipes the previous setup.
- UserPreferences gains activeAIConfigId; legacy single-config
columns are mirrored from the active profile so existing reads
keep working without conditional logic.
- Idempotent boot migration lifts any existing single-config row
into a default profile.
2. Ollama auto-detect
- The "Add config" form probes /api/tags on the StartOS internal
addresses (ollama.startos / ollama.embassy on :11434). If
reachable: URL pre-fills, model field becomes a dropdown of
installed models. Fixes the copy-paste UX.
3. Curated model dropdowns for major providers
- Claude: Opus 4.7, Sonnet 4.6 (1M ctx), Haiku 4.5
- OpenAI: GPT-5.5, 5.4, 5.4-mini, 5.4-nano
- Gemini: 3.1-pro-preview, 2.5-pro, 2.5-flash, etc.
- "Other (type your own)" stays for niche models.
- Fixes "I tried gemini-3.0-pro and got 404."
4. Background generation
- lib/ai/generationRunner.ts: detached runner with in-memory
pub/sub bus. POST /api/ai/generate kicks it off and returns
immediately. SSE stream attaches by id. The runner survives
request cancellation; navigating away no longer kills it.
- New AIGeneration columns: progressText (in-flight stream),
durationMs (final wall-clock).
- Generate UI shows a banner explaining background-safety.
- History detail page polls progress + renders partial JSON
live for cross-process resume (page refresh, new tab).
5. System prompt overhaul
- lib/ai/systemPromptBase.ts: structural contract prepended to
every template. Forces JSON-only output, library-exerciseId
usage (kills "exerciseId doesn't belong to this user" errors),
and per-resistance-exercise suggestedWeight (with-history vs
without-history variants).
- aiExerciseSchema + ProgramExercise gain suggestedWeight +
suggestedWeightUnit. Starting a workout from a ProgramDay
pre-populates SetLog.weight from the suggestion.
6. Test connection improvements
- Latency in seconds (was ms — confusing for slow Ollama).
- Stale "✓ Connected" clears on form change.
- Per-config Test (no need to activate first).
- Generous maxOutputTokens for thinking models.
- Gemini surfaces finishReason on empty response (e.g. "blocked
by safety filter") instead of generic "empty response."
- Test endpoint accepts a draft body so you can verify before
saving + before activating.
7. History detail view
- Click row → full program tree + exact prompts sent. Apply from
here without re-generating. Pending rows poll for progress.
8. Sidebar sub-navigation
- AI: Generate / History / Templates
- Settings: General / Password / Sessions / AI integration /
Export / Instance (admin) / Danger zone, with anchor scroll.
9. API key UX
- "Key saved" indicator on saved configs (was confusing to see
an empty input after a successful save).
Schema migrations (additive, idempotent in entrypoint):
- AIConfigProfile table created
- UserPreferences.activeAIConfigId
- AIGeneration.progressText + durationMs
- ProgramExercise.suggestedWeight + suggestedWeightUnit
Tests: 16 new (systemPromptBase, modelMenu, generationRunner). 177
total pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four incremental upgrades to the AI program generator. No schema change, no /data migration.
1. History as context (the killer feature)
- lib/ai/historyContext.ts builds a 90-day per-exercise rollup:
frequency, recent weights, estimated 1RM (Epley), avg RPE,
days-since-last, plus a STAGNANT flag when the heaviest weight in
the new half doesn't beat the old half.
- Generate page surfaces an "Include my workout history as context"
checkbox (default on at >=10 logged workouts). When checked, the
~1-3 KB summary is appended to the system prompt so the model can
recommend things like "you've stalled bench at 245 — try paused reps."
- We deliberately don't ship raw set logs (privacy + token cost).
2. Test connection
- POST /api/ai/test sends a tiny "say hi in 3 words" prompt and
reports latency + first sample, or the error inline.
- "Test connection" button next to "Save AI config" in
Settings -> AI integration. Verifies provider/model/key/baseUrl
without going through full program generation.
3. Cost estimator
- lib/ai/pricing.ts ships a price table for major models
(Claude 3.5/3.7/4/4.5, GPT-4o/5/o1/o3/o4-mini, Gemini 1.5/2.0/2.5).
Ollama always returns 0; openai-compatible returns null.
- Generation history shows per-row cost + a 30-day rolling total
at the top of the page.
4. Streaming preview render
- lib/ai/lenientJson.ts: stack-aware partial-JSON parser that
auto-closes open strings/brackets/braces in reverse-of-opening
order, drops dangling key:value pairs and partial keywords.
Returns a best-effort snapshot of the program-so-far on each chunk.
- Generate UI now renders a live "Building program..." panel that
updates as weeks/days/exercises arrive instead of just showing
raw text and waiting for stream end.
Tests: 26 new (ai-historyContext.test.ts, ai-lenientJson.test.ts,
ai-pricing.test.ts). 161 total pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>