# Task — Emulator basket worker (Phase 1)

**Status:** Implemented (manual E2E + Ops migrate pending local Docker)  
**Date:** 2026-10-06

## Summary
After Paystack checkout, Baskit Ops enqueues per-retailer `build_basket` jobs. A Python worker on the developer laptop polls with a service key, clones a golden Android AVD per user, and uses uiautomator2 drivers to fill retailer carts (no payment in store apps). Flutter exposes a lazy `/setup` wizard and order-level basket status.

## Test cases
1. **Ops schema** — `customer_emulators`, `emulator_jobs`, extended `retailer_connections` and `order_items.barcode`; `php spark migrate` applies cleanly.
2. **Customer API** — session token: register emulator, poll status, connect retailer with `login_phone`, submit OTP, `GET orders/{code}/baskets`.
3. **Worker API** — `X-Service-Key` claim/heartbeat/complete/fail; rate limit and IP allow-list via filters.
4. **Order hook** — paid order enqueues jobs only for connected retailers; unconnected retailers appear as `skipped`.
5. **Worker unit tests** — in [emulator_worker](https://bitbucket.org/overdrive/emulator_worker): `pytest tests -m "not live"` (matching, PAYMENT_WORDS guard, Ops client paths).
6. **Flutter** — `flutter analyze`, `flutter test` including `api_client_emulator_test`, `setup_model_test`, `setup_flow_test`; register → `/setup` navigates steps.
7. **Manual E2E** — golden AVD `baskit_golden`, worker running, connect Sixty60 with OTP, checkout 3 items, baskets show `done` and cart matches (no retailer checkout).
8. **Negative** — wrong OTP shows error; worker stopped → job reclaimed after stale interval.

## Related
- [overdrive/emulator_worker](https://bitbucket.org/overdrive/emulator_worker) — Python poll worker (dedicated host)
- `lib/features/setup/*`, `lib/models/emulator_status.dart`, `lib/core/api_client.dart`
- Ops (sibling repo): `CreateEmulatorWorker` migration, `EmulatorJobService`, `Api/Emulator`, `Worker/Jobs`
- Docs: `docs/tasks/TASK-003.md`, `docs/tasks/TASK-006.md`
