TICKET-006 (frontend): barcode scanner, OFF scan/search flows, recent foods
- 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
This commit is contained in:
+79
-37
@@ -5,9 +5,11 @@ first; this file only records progress and session-specific notes.
|
||||
|
||||
## Where we are in the plan
|
||||
|
||||
**Milestone M1 (Manual calorie tracker) is COMPLETE.** Tickets 001–005 all
|
||||
implemented, QA-verified, and committed. The app is usable end-to-end for
|
||||
manual food entry and daily logging.
|
||||
**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 |
|
||||
|--------|--------|--------|
|
||||
@@ -16,12 +18,12 @@ manual food entry and daily logging.
|
||||
| 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 | ⬜ **next** | — |
|
||||
| 007 Meals (from-log, unpack, recursion) | ⬜ pending | — |
|
||||
| 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 **104 passed** (`cd backend && uv run pytest`),
|
||||
frontend vitest + `npm run build` green.
|
||||
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
|
||||
|
||||
@@ -42,40 +44,80 @@ frontend vitest + `npm run build` green.
|
||||
layout. All HTTP via `lib/api.js`; shared state in `stores.svelte.js`;
|
||||
Svelte 5 runes only.
|
||||
|
||||
## Process lessons (important for the next orchestrator)
|
||||
## Process notes (session 2)
|
||||
|
||||
1. **Implementer agents can falsely report success without writing code.**
|
||||
This happened twice (TICKET-004 backend, first TICKET-005 frontend attempt).
|
||||
Mitigations that worked:
|
||||
- Every implementer task must require `git status --short` evidence and a
|
||||
live smoke test (curl the endpoint / build + serve) in its report.
|
||||
- QA must be explicitly told not to trust the implementer's report and to
|
||||
verify the feature exists (e.g. check `/openapi.json` routes) before
|
||||
testing.
|
||||
2. QA (playwright) genuinely catches real bugs — it found broken date-nav
|
||||
buttons and a stale-summary bug in TICKET-005; both were fixed and
|
||||
re-verified before commit.
|
||||
3. Backend servers go stale between tickets (old uvicorn missing new routes).
|
||||
QA handles restarts, but expect it.
|
||||
- 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-006 (Milestone M2)
|
||||
## Where to pick up: TICKET-007 (Milestone M3 — Meals)
|
||||
|
||||
OFF normalization + barcode scan & search flows. Depends on 005 (done).
|
||||
Read the TICKET-006 section of `IMPLEMENTATION_PLAN.md` — key points:
|
||||
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:
|
||||
|
||||
- Backend: OFF → foods normalization in exactly one module (kcal/kJ mapping,
|
||||
User-Agent, timeouts); `GET /api/off/product/{barcode}`, `GET /api/off/search`,
|
||||
`POST /api/off/refresh/{food_id}`; restore-on-rescan rule (§3.1); implement
|
||||
`GET /api/foods/recent`; httpx mocked at the boundary in tests.
|
||||
- Frontend: `BarcodeScanner.svelte` + `lib/scanner.js` (native
|
||||
`BarcodeDetector` with lazy zxing-wasm fallback, ~3–5 fps decode loop,
|
||||
camera teardown on destroy); scan flow per §4.1 with manual barcode
|
||||
fallback; OFF fallback in search per §4.2; recent foods surfaced in UI.
|
||||
- **Camera testing caveat (from the user):** a webcam exists but real scanner
|
||||
verification (esp. the §8.4 phone checklist — Android Chrome, iOS Safari,
|
||||
EAN-13/UPC-A) needs the user manually over Caddy HTTPS. Don't block the
|
||||
ticket on camera QA: verify the manual-barcode fallback and OFF search
|
||||
flows via playwright, and mark the phone checklist as pending user testing.
|
||||
- **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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user