Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ design. Public repo: **github.com/serpeigd/TrainFitter**.
[`docs/decisiones.md`](docs/decisiones.md) — read it only if you need the
*why* behind a past call; don't load it by default (it's long).
- [`docs/highlights.md`](docs/highlights.md) is the condensed, interview-ready
version of the same log (18 decisions, ~2 pages) — update it when a change adds
version of the same log (22 decisions, ~2 pages) — update it when a change adds
a genuinely new "defensible decision," not for routine work.
- **Scheduled documentation-sync runs (added 2026-08-07, explicit decision in
chat): standing authorization to merge doc-only PRs from that recurring task
Expand All @@ -54,9 +54,12 @@ design. Public repo: **github.com/serpeigd/TrainFitter**.
Client intake JSON → routine_agent + diet_agent (motor="reglas"|"llm", same output schema)
→ validator_agent (ALWAYS rule-based, re-derives risk from raw profile)
→ verdict: aprobado_automatico | revision_reforzada
→ aprobado_automatico: sent automatically (mcp/gmail_client.py's enviar_plan(),
as of 2026-08-19 — see below)
→ revision_reforzada: human review, ALWAYS, no auto-send (unchanged)
→ aprobado_automatico: sent automatically, zero clicks (mcp/gmail_client.py's
enviar_plan(), as of 2026-08-19 — see below)
→ revision_reforzada: human review, ALWAYS, before anything sends — but once
approved, "send" now means enviar_plan() directly too (a real send, no draft
in between); a Gmail draft is a secondary, explicit fallback button, not the
default path (see below)
```

- **Two interchangeable engines** per generation agent: `"reglas"` (default,
Expand Down
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,25 @@ This repository is built phase by phase, as a learning project. Right now:
[`docs/base_conocimiento/sinergias_nutrientes.md`](docs/base_conocimiento/sinergias_nutrientes.md)
— not just listed as a separate tip. Renders as a styled table in the diet
PDF and in the trainer's on-screen review, in whichever language the UI
is set to. A client can **like a meal from their own portal**, and it
biases (never forces — same "prefer, don't lock" philosophy as the
synergy pairing above) toward reappearing in their next generated week,
dropped automatically if it's no longer a safe/valid pick (e.g. a new
allergy) — verified against the real workspace, not just mocked: a real
liked meal reappeared in ~55% of 15 regenerations, matching the ~60%
design target. **Liking an exercise works exactly the same way** on the
routine side (see [`agents/rutina_reglas.py`](agents/rutina_reglas.py)'s
is set to. A client can **like or dislike a meal from their own portal**
(both undoable — a like/dislike toggles back off the same way it was
set), and a like biases (never forces — same "prefer, don't lock"
philosophy as the synergy pairing above) toward reappearing in their next
generated week, while a dislike biases the same meal combination away
from a future week; either is dropped automatically if it's no longer a
safe/valid pick (e.g. a new allergy) — verified against the real
workspace, not just mocked: a real liked meal reappeared in ~55% of 15
regenerations, matching the ~60% design target. **The same bias mechanism
exists on the routine side** (see
[`agents/rutina_reglas.py`](agents/rutina_reglas.py)'s
`_sesgar_por_favoritos()`): a liked exercise is preferred, never pinned,
and is dropped silently the moment a new injury makes it unsafe — the
safety cross-check always outranks the preference. Verified live against
a real client record: a liked exercise came back in ~74% of 30
regenerations.
regenerations. Unlike meals, exercise-liking has no portal UI anymore —
it was removed there by direct request, so today it's only ever set on a
client's stored profile directly; the bias itself still runs whenever
`ejercicios_favoritos` is present.
- Both rule engines use most of what the intake form actually collects:
training level and session length shape routine volume/complexity,
disliked foods and lifestyle signals (stress/sleep, job type) bias diet
Expand Down Expand Up @@ -358,11 +364,6 @@ actually been verified:
- **A magic link can be revoked early, but only by hand in Notion** — clearing the
client's "Portal Reference" property invalidates it immediately. There's no
"revoke" button in the app itself; the default is still its own expiry (7 days).
- **A liked meal/exercise in the client portal can't be un-liked from the portal
itself.** The 🤍/❤️ button is one-way by design (see `ui/app.py`'s
`_vista_portal_cliente()`) — `agregar_comida_favorita()`/`agregar_ejercicio_favorito()`
only ever append; removing one means editing "Liked Meals (JSON)"/"Liked Exercises
(JSON)" in Notion directly.
- **The Streamlit Community Cloud free tier sleeps after inactivity.** The first request
after a period of no traffic triggers a cold start (can take up to a minute); this is a
hosting trade-off, not an application bug.
Expand Down
23 changes: 23 additions & 0 deletions docs/highlights.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,5 +273,28 @@ what caught it, not testing harder.

---

## 22. A second reversal of the same guarantee — scoped narrower, not looser

#19 kept `revision_reforzada` completely untouched when `aprobado_automatico`
plans started sending themselves: still a mandatory draft, still a human
send, no exceptions. That held right up until the trainer's own review moved
*into* the panel itself — the new edit controls (#21) plus a
password-gated Approve click already are the review, so a second look inside
Gmail afterward added friction, not safety. `_panel_aprobacion()`'s primary
action is now "Enviar el plan por email," calling `enviar_plan()` directly;
"Crear borrador en Gmail en su lugar" stays as an explicit secondary button
for a trainer who wants a second look anyway, and as the real fallback if
the send itself fails. `revision_reforzada` is now the *only* category left
in this project that gates a real, no-draft-in-between send behind a
password — the same reasoning as #19, applied one step further once the
thing that reasoning depended on (a human already looked) became true here
too. **Why it matters:** the guarantee didn't get weaker twice by
coincidence — each reversal was justified by a *specific* mechanism (the
validator's own verdict, then the panel's own review) actually satisfying
what the removed step used to provide, not by "we already crossed this line
once."

---

*For the full "why," including things that were tried and reverted, see*
[`decisiones.md`](decisiones.md).
Loading