4dd44b08d0
- lib/scanner.js: native BarcodeDetector + lazy zxing-wasm fallback, ~4fps decode loop, camera teardown in stop() (§8.2 rule 6) - BarcodeScanner.svelte: getUserMedia camera, permission-denied → message + manual barcode text input fallback, stop on first detect + on destroy - App.svelte: §4.1 scan-flow state machine (localFound/offFound/notFound/ error) — local-by-barcode first, OFF lookup → pre-fill FoodEditor → save → log, Refresh-from-OFF button, not-found → manual/search options - FoodEditor.svelte: optional food prop pre-fills from OFF (read-only barcode, source badge), post-save log flow - FoodSearch.svelte: OFF fallback (§4.2) when local results empty - Dashboard.svelte: recent foods quick-log chips - api.js: searchFoodsByBarcode, offRefresh, updateFood, recentFoods - scanner.test.js: 2 unit tests for pure helpers - §8.4 phone checklist deferred to user testing over Caddy HTTPS; manual-barcode + OFF search flows playwright-verified - HANDOFF.md updated: M1+M2 complete, 007 next
133 lines
7.5 KiB
Markdown
133 lines
7.5 KiB
Markdown
# 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 001–006 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) | ⬜ **next** | — |
|
||
| 008 Food library view + restore | ⬜ pending | — |
|
||
|
||
Test counts at HEAD: backend **152 passed** (`cd backend && uv run pytest`),
|
||
frontend **18 vitest passed** + `npm run build` green.
|
||
|
||
## What was done this session
|
||
|
||
- Reviewed codebase/spec/plan; confirmed only scaffold existed.
|
||
- Ran tickets 001–005 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).
|