Files
calcount/HANDOFF.md
T

178 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CalCount — Session Handoff (2026-07-26)
Status snapshot for the next worker. Read `SPEC.md` and `IMPLEMENTATION_PLAN.md`
first; this file only records progress and session-specific notes.
## Where we are in the plan
**Milestones M1 (Manual calorie tracker) and M2 (Barcode scanning & OFF
integration) are COMPLETE.** Tickets 001006 all implemented, QA-verified,
and committed. The app is usable end-to-end for manual food entry, daily
logging, barcode scanning (manual-barcode path verified; phone camera path
pending user testing), and OpenFoodFacts lookup/search.
| Ticket | Status | Commit |
|--------|--------|--------|
| 001 Foods CRUD + soft-delete | ✅ done, QA passed | `3f2c765` |
| 002 Targets CRUD (single-active invariant) | ✅ done, QA passed | `601c0d4` |
| 003 Daily log write path | ✅ done, QA passed | `87d7eca` |
| 004 Day summary endpoint | ✅ done, QA passed | `5372e8c` |
| 005 Frontend daily view | ✅ done, QA passed | `b69661c` |
| 006 OFF + barcode scan/search flows | ✅ done, QA passed (backend `32461b7`, frontend below) | `32461b7` + frontend |
| 007 Meals (from-log, unpack, recursion) | ✅ done, QA passed | `a8aed9a` + `7db6484` |
| 008 Food library view + restore | ✅ done, QA passed | `0c239cd` + `731e541` |
**All milestones M1M4 COMPLETE — v1 feature-complete.**
Test counts at HEAD: backend **180 passed** (`cd backend && uv run pytest`),
frontend **23 vitest passed** + `npm run build` green.
## TICKET-008 notes (session 3, continued)
- Backend: `POST /api/foods/{id}/restore` (idempotent — restoring a live food
returns it unchanged; 404 unknown id; nutrition/created_at/history intact).
- Frontend: `FoodLibrary.svelte` (was a scaffold placeholder) — search,
limit/offset pagination (fetches PAGE_SIZE+1 to detect next page),
include_deleted toggle, confirm-then-soft-delete, restore. Edit routes
through FoodEditor: regular foods get a new edit mode (`food.id` present →
`api.updateFood`, preserves `is_meal`); meals route to the ticket-007 meal
component editor. Nav via "🍔 Foods" button on dashboard. Editor closes call
`refreshDayData()` since food edits retroactively change log rendering (§2.1).
- Gotcha fixed: initial load must use `onMount`, not `$effect` — an effect
tracks the search-box state and re-fetches on every keystroke.
## After v1
Remaining loose ends: phone-camera scan checklist (§8.4, needs Caddy HTTPS +
user testing), README.md user edit uncommitted (user's call), deferred spec §5
items (deployment, CSV export, extra nutrients in UI, PWA manifest).
## TICKET-007 notes (session 3)
- Backend: `routers/meals.py` (from-log, unpack, PUT components — each one
transaction), recursive meal nutrition + cycle detection in
`services/nutrition.py`/`services/meals.py`; ticket-004 TODO removed —
summary, `GET /api/foods/{id}` (MealRead w/ `computed_nutrition_per_meal`),
and `GET /api/log` (nested `components` in LogFoodRead) all show real
derived meal nutrition. Unpack scaling verified (1.5× meal → 1.5× each
component); cycles (direct + transitive) → 422.
- Frontend: Dashboard "☑ Select entries" → multi-select → MealBuilder overlay
(save-as-meal); LogEntry meal rows collapsible + 🔓 Unpack + "Edit
components" → FoodEditor meal mode (`appView='editMeal'`,
`mealEdit.foodId` in stores); meal kcal fix in `format.js`
(`caloriesForEntry` uses backend `computed_nutrition`, not
`calories_per_unit` which is null for meals).
- **Process:** both implementer subagent runs this session returned truncated
output mid-implementation (rate limits?) — the second one wrote nothing.
Orchestrator implemented the meal component editor + the select-mode entry
point fix directly. QA agent (playwright-cli) worked well both runs and
caught the save-as-meal chicken-and-egg UX bug (checkboxes rendered only
when already selecting). Consider checking subagent reports against `git
status` before trusting them.
## What was done this session
- Reviewed codebase/spec/plan; confirmed only scaffold existed.
- Ran tickets 001005 through the agentic flow: `be-implementer` /
`fe-implementer` build, `qa` verifies independently (curl for backend-only
tickets, playwright-cli browser automation for frontend), orchestrator
commits between tickets.
- Backend now has: full foods CRUD with soft-delete + shared query helpers,
targets with transactional single-active invariant + historical lookup,
daily log CRUD with embedded food payloads, and `/api/log/summary` with all
nutrition math consolidated in `services/nutrition.py` (weight vs count
scaling; meals intentionally contribute 0 — marked TODO for TICKET-007).
- Frontend now has: dashboard with progress bar vs target, meal-slot grouping,
inline edit/delete, date navigation (UTC-safe `shiftDate` in
`lib/format.js`), manual Add Food form, search-and-log flow with live
preview, minimal target form, loading/error/empty states, mobile-first
layout. All HTTP via `lib/api.js`; shared state in `stores.svelte.js`;
Svelte 5 runes only.
## Process notes (session 2)
- Baked the retrospective's structural fixes into the agent definitions
(chore `f8048da`): be/fe-implementer now MUST paste `git status`, test
output, and a live smoke test; qa is adversarial (distrust self-reports,
confirm features via `/openapi.json`), restarts both servers + resets the
dev DB before testing, and writes expected numbers into scenarios. This
eliminated the false-success-report failure mode on 006.
- `.playwright-cli/` + `*.png` are now gitignored; QA writes artifacts to
`/tmp`.
- The scout agent earned its keep on 006: primed exact seams for both
stacks, saving re-derivation. Its one slip (recent-foods ordering by
`created_at` vs `daily_log` appearance) was caught and corrected in the
implementer prompt.
- Split 006 into backend → QA(curl) → commit, then frontend → QA(playwright)
→ commit, with an orchestrator diff review before each QA run. Two commits
for one ticket (across stacks) gave clean checkpoints.
- Bug-fix loop on 006 backend (OFF search 500 on upstream 503) was cheap and
effective — one focused implementer call + verify.
## Where to pick up: TICKET-007 (Milestone M3 — Meals)
Meals: `meal_components` table, `POST /api/meals/from-log`, `POST
/api/meals/{meal_id}/unpack`, `PUT /api/meals/{meal_id}/components`, and
real derived meal nutrition (recursive component summation + cycle
detection in `services/nutrition.py`). Read the TICKET-007 section of
`IMPLEMENTATION_PLAN.md` — key points:
- **This is the highest-complexity ticket.** Recursion, cycle detection,
and transactional rollback are the spec's named testing priorities
(§8.4). Build the service layer first with direct unit tests, then wire
routers. Run implementer and QA as **separate** calls (not a chain) with
an orchestrator diff review in between (retrospective lesson #11).
- Backend: `POST /api/meals/from-log` and `POST /api/meals/{meal_id}/unpack`
are one-transaction-each (commit once or roll back entirely — §8.1 rule 6).
`PUT /api/meals/{meal_id}/components` replaces the component list wholesale,
cycle-checked. Meal nutrition = recursive component summation with a
visited-set, both for nutrition reads and cycle checks on write.
- Remove the ticket-004 TODO (meals contributing 0 to summary). Summary
(ticket 004), `GET /api/foods/{id}`, and `GET /api/log` responses must now
show real derived meal nutrition; log responses include meal components
nested for the collapsible UI (§3.3).
- Unpack quantity scaling (§3.2): each component's quantity × the original
meal entry's scaling factor (1.5× meal → 1.5× each component). Pin this
with a dedicated test.
- Cycle attempts (meal containing itself, directly or transitively) → 422.
- Frontend: "Save as Meal" (multi-select today's entries → name → replace
with meal entry), collapsible meal rows (collapsed = name + total kcal,
expanded = components), "Unpack" action, meal component editing from the
food editor for `is_meal` foods (§4.7).
## TICKET-006 notes (for reference / loose ends)
- Backend OFF normalizer is `backend/services/off.py` (the ONE module,
§8.1 rule 10): kcal/kJ fallback, not-found rule, graceful degradation on
upstream 503/timeout (returns `None`/`[]`, never 500). `GET /api/off/product`,
`/api/off/search`, `POST /api/off/refresh/{food_id}` (404 unknown id, 400
no-barcode). Restore-on-rescan in `services/foods.py` `create_food()`.
`GET /api/foods/recent` orders by `daily_log.created_at` (most recent log
ACTION, deliberately NOT by the log `date` field — see docstring; QA once
read this as a bug, it's a tested, deliberate choice).
- Frontend scan flow lives in `App.svelte` as a phase state machine
(`null`/`localFound`/`offFound`/`notFound`/`error`). `lib/scanner.js` is
the camera+decode loop (native BarcodeDetector + lazy zxing-wasm,
~4fps, teardown in `stop()`). `BarcodeScanner.svelte` stops on first
detect + on destroy (§8.2 rule 6).
- **§8.4 phone checklist PENDING USER TESTING** (native BarcodeDetector on
Android Chrome, zxing-wasm on iOS Safari, EAN-13/UPC-A decode). Requires
Caddy HTTPS. The manual-barcode fallback + OFF search flows are
playwright-verified; do not regress them.
- **Dev-only Vite proxy caching** observed by QA: empty OFF search
responses were cached within a Vite dev session; a fresh Vite restart
cleared it. Not a code defect (production build unaffected). If it
recurs, check the vite proxy config (`changeOrigin`, cache headers).
## Loose ends / chores
- `.playwright-cli/` artifacts are polluting the repo (some were even tracked
in git before this session). Recommend: `git rm -r --cached .playwright-cli`,
add it (and `*.png` QA screenshots) to `.gitignore`, commit.
- `README.md` has an uncommitted user edit ("Agentic dev" section) — left
untouched deliberately; commit or discard at the user's discretion.
- Dev DB (`backend/calcount.db`) contains QA test data ("QA Porridge", a
2000 kcal target). Reset by stopping uvicorn, deleting the file, restarting
(migrations recreate the schema on startup).
- Leftover background processes may be running (uvicorn :8000, vite :5173).