Plan: mcd-tracker
Plan: mcd-tracker
Vision
Projects & Repos Touched
| Project/Repo | Platform | Role in this plan |
|---|---|---|
| mcd-tracker (this project) | Forgejo | Parent project — all repos below |
| mcd-tracker-api | Forgejo | FastAPI backend (Phase 2-5) |
| mcd-tracker-playground | Forgejo | HTML/CSS prototypes (Phase 6) |
| mcd-tracker-app | Forgejo | SvelteKit frontend (Phase 7) |
| mcd-tracker-ios | Forgejo | Swift/SwiftUI iOS app (Phase 9-10) |
| pal-e-services | Forgejo | Service onboarding terraform (Phase 1, 7) |
| pal-e-deployments | Forgejo | Kustomize overlays (Phase 1, 3, 7) |
Context
- [x] Project created in pal-e-docs (
project-mcd-tracker) - [x] Project page created with user stories, architecture stubs, repo table
- [x] Plan created (this note)
- [ ] Board created
- [ ] Architecture diagram notes created
Previous Plan
Depends On
plan-pal-e-platform— platform must be stable (it is: 16/19 phases completed)plan-pal-e-agency— SOPs and conventions must exist (they do: all 13 phases resolved)
| Decision | Rationale |
|---|---|
| Python FastAPI + Postgres (CNPG sidecar) | Matches basketball-api pattern exactly. Proven stack. |
| Dedicated Keycloak realm <code>mcd-tracker</code> | Separate user base. Enterprise-ready multi-tenancy. |
| Rolling 30-day window, not calendar month | McDonald's enforces per-code. Each usage starts an independent 30-day timer. |
| 5-day redemption window (2026-03-16) | BOGO codes expire 5 days after earning. Two timers per code: 5-day use-it-or-lose-it + 30-day slot window. |
| DevOps first, code second | Production namespace + CI + ArgoCD ready before first line of code. |
| Receipt-first workflow (2026-03-16) | Camera → OCR → survey → BOGO code → save. Two codes (survey + BOGO). Receipt is a first-class entity with photo proof. |
| Capacitor for iOS, not Swift (2026-03-16) | SvelteKit + Capacitor = one codebase for web + iOS. Eliminates separate Swift app. |
| Frontend: playground → SvelteKit+Capacitor (2 stages) | Was 3 stages. Capacitor collapses SvelteKit+iOS into one stage. |
| MacBook Air M1 as Woodpecker agent | Needed for Xcode builds. Capacitor generates Xcode project, agent runs <code>npx cap sync && xcodebuild</code>. |
| keycloak-js for SPA auth, not Auth.js (2026-03-16) | Auth.js requires SSR. SPA mode for Capacitor means client-side OIDC only. See <code>project-capacitor-mobile</code> auth-decision. |
| Location-centric UX, not flat code list (2026-03-16) | Codes belong to locations. Home = location dashboard. Task-oriented, not data-dump. |
| Gamification: XP + levels, not $ saved (2026-03-16) | Codes redeemed tally + levels. Dollar amounts unreliable (prices change). Celebrate usage, not savings. |
| Receipt intelligence (2026-03-16) | Receipts contain rich data beyond survey codes. Future: item analysis, spending patterns, optimal visit timing. Scoped as Phase 14. |
Phases
Phase 1: Service Onboarding (COMPLETED)
forgejo_admin/pal-e-services, forgejo_admin/pal-e-deployments- Add
mcd-trackerentry topal-e-services/terraform/k3s.tfvarsvar.services map:mcd-tracker = { forgejo_repo = "forgejo_admin/mcd-tracker-api" image_repo = "mcd-tracker/api" port = 8000 funnel = true source_repo = "forgejo_admin/pal-e-deployments" source_path = "overlays/mcd-tracker/prod" } tofu plan -lock=false→ verify 6-7 new resources (namespace, Harbor project, robot accounts, pull secret, ArgoCD app, funnel ingress)tofu apply -lock=false- Create kustomize overlay:
pal-e-deployments/overlays/mcd-tracker/prod/
Phase 2: Backend Scaffold + First Deploy (COMPLETED)
forgejo_admin/mcd-tracker-api- Create
mcd-tracker-apirepo on Forgejo - Scaffold FastAPI app (following basketball-api pattern):
- Activate Woodpecker → add Harbor secrets (
harbor_username,harbor_password) - Push → green pipeline → Harbor image → ArgoCD deploys
- Verify:
curl https://mcd-tracker.tail5b443a.ts.net/healthz
Phase 3: Data Model + Postgres (COMPLETED)
forgejo_admin/mcd-tracker-api, forgejo_admin/pal-e-deployments- Add Postgres sidecar to kustomize overlay (
postgres.yaml— Postgres 16-alpine, 1Gi PVC, ClusterIP service) - Add
src/mcd_tracker_api/database.py— engine, SessionLocal, get_db() - Add
src/mcd_tracker_api/models.py— SQLAlchemy models: - Init Alembic, create first migration
- Update health endpoint to verify DB connection
- Deploy — verify migration runs on startup
- No separate User table — user identity comes from Keycloak JWT (
subclaim). Storekeycloak_subas FK-like reference on Location and CouponUsage. - Rolling window query:
SELECT COUNT(*) FROM coupon_usage WHERE location_id = ? AND user_id = ? AND used_at > NOW() - INTERVAL '30 days' - Slot reopens:
MIN(used_at) + 30 daysfrom the 5 active usages at a location
Phase 4: Keycloak Realm + Auth (COMPLETED)
forgejo_admin/mcd-tracker-api- Create
mcd-trackerrealm in Keycloak admin console - Create realm roles:
user,admin - Create OIDC client:
mcd-tracker-app(for SvelteKit) andmcd-tracker-ios(for Swift, public client + PKCE) - Add
src/mcd_tracker_api/auth.py— JWKS fetch, JWT decode, User dataclass, require_role dependency factory (copy basketball-api pattern) - Add config:
MCD_TRACKER_KEYCLOAK_REALM_URL - Update deployment-patch.yaml with Keycloak env var
- Create test user + admin user in realm
Phase 5: Core API Endpoints + Integration Tests (COMPLETED)
forgejo_admin/mcd-tracker-api- Routes:
- Rolling window logic:
- Integration tests (pytest, real Postgres):
- Deploy and verify via curl
Phase 6: Frontend Playground (IN PROGRESS — CSS consolidated, @-comment specs added, pending phone approval)
forgejo_admin/mcd-tracker-playground- Decide: folder experiment in
html-playgroundor repo experiment (mcd-tracker-playground) - Create screens (vanilla HTML/CSS, hardcoded data):
- Mobile-first design — this is a phone app at heart
- Lucas verifies on phone via Tailscale funnel
- Iterate until design locks
sop-capacitor-mobile-lifecycle created. Pending: Lucas phone approval (Gate 1).Phase 7: SvelteKit + Capacitor Frontend (IN PROGRESS — scaffold deployed, Docker Compose merged, pending local validation)
forgejo_admin/mcd-tracker-app, forgejo_admin/pal-e-services, forgejo_admin/pal-e-deployments- Create
mcd-tracker-apprepo on Forgejo - SvelteKit scaffold with Auth.js + Keycloak OIDC (copy westside-app pattern)
- Direct port from playground: copy CSS → scoped styles, copy HTML → Svelte templates, replace hardcoded data with
{data.foo}bindings - Pages:
/(dashboard),/add(log code),/locations(manage),/history,/admin(stats) - Service onboarding for frontend: add
mcd-tracker-appto k3s.tfvars + kustomize overlay - Deploy, verify on phone
Phase 8: Mac Woodpecker Agent (NOT STARTED)
forgejo_admin/pal-e-platform (agent config docs)- Install Xcode on MacBook Air M1 (App Store)
- Install Woodpecker agent binary (
brew install woodpecker-ci/tap/woodpecker-agentor direct download) - Configure agent with local backend:
WOODPECKER_SERVER=https://woodpecker.tail5b443a.ts.net WOODPECKER_AGENT_SECRET=<from k3s.tfvars> WOODPECKER_BACKEND=local WOODPECKER_FILTER_LABELS=platform=darwin - Create launchd plist for auto-start on boot
- Install Fastlane:
brew install fastlane - Verify: agent appears in Woodpecker UI, test pipeline with
labels: [platform: darwin]runs on Mac
Phase 9: Capacitor iOS Build + Native Plugins (NOT STARTED — replaces Swift app)
forgejo_admin/mcd-tracker-ios- Create
mcd-tracker-iosrepo on Forgejo - SwiftUI app scaffold:
- Keycloak OIDC auth via
ASWebAuthenticationSession(public client + PKCE —mcd-tracker-iosclient created in Phase 4) - API client layer — same endpoints as SvelteKit, JWT Bearer auth
.woodpecker.yamlwithlabels: [platform: darwin]— targets Mac agent- Pipeline:
xcodebuild test→xcodebuild archive→ export .ipa
Phase 10: App Store Submission (NOT STARTED)
forgejo_admin/mcd-tracker-ios- Enroll in Apple Developer Program ($99/yr)
- Fastlane setup:
- CI pipeline addition: after
xcodebuild archive, runfastlane pilot upload - TestFlight beta distribution — Lucas + testers validate
- App Store submission — metadata, screenshots, privacy policy, review
- Post-approval: pipeline does git push → test → build → TestFlight → (manual promote to App Store)
Phase 11: Analytics + Dual-Channel Tracking (NOT STARTED)
Goal: Track user behavior across web + iOS. Know how many people use the app, what they do, and which channel they prefer.
Owner: Dev agent + Betty Sue
Repos: mcd-tracker-app, pal-e-services (Umami onboarding)
Depends on: Phase 10 (App Store live)
Scope:
- Deploy Umami or Plausible to cluster (pal-e-services onboarding — same pattern as any service)
- Embed Umami tracking script in mcd-tracker-app (works on both web and iOS/Capacitor)
- Add
POST /eventsendpoint to mcd-tracker-api — custom product events (scanned receipt, redeemed code, slot checked) - App Store Connect already provides download/impression/crash metrics for free
- Build a simple admin dashboard showing: active users, web vs iOS split, most popular actions, most used locations
Phase 12: Monetization (NOT STARTED — when ~100 active users)
Goal: Freemium model with paywalls. Free tier for casual use, paid for power users.
Owner: Dev agent + Lucas
Repos: mcd-tracker-api, mcd-tracker-app
Depends on: Phase 11 (analytics — need to understand usage patterns before monetizing)
Scope:
- Define free vs paid tiers (e.g., free: 3 locations, paid: unlimited + push notifications + stats)
- Stripe integration for web payments (proven pattern from basketball-api subscriptions)
- Apple In-App Purchases for iOS (Capacitor plugin: RevenueCat or
@capgo/capacitor-purchases) - RevenueCat for unified subscription management across web + iOS
- Feature flags in API: check subscription tier before allowing premium features
- Landing page + App Store description updated with pricing
Phase 13: Gamification + XP System (NOT STARTED)
Goal: Celebrate usage. XP system with levels, lifetime code tally, mascot character with context-aware reactions.
Owner: Lucas (mascot design) + Dev agent (implementation)
Repos: mcd-tracker-api, mcd-tracker-app
Depends on: Phase 7 (frontend must exist)
Scope:
- API:
GET /statsendpoint — lifetime codes redeemed, current level, XP progress, streak days - Level system: Level 1 (0-5 redeemed) → Level 2 (6-15) → etc. Titles like "BOGO Beginner" → "Free Food Fanatic" → "Coupon Legend"
- Home page XP banner above location list ("Level 3 BOGO Hunter · 12 codes redeemed")
- Mascot character — Lottie animations, ~8 states: idle, happy, excited, worried (code expiring), sleeping (no activity), celebrating (just redeemed), nudging (slot opened), waving (first visit)
- Mascot appears on: home banner, redemption overlay, empty states, push notifications (future)
- Design mascot as separate creative task — Lucas drives, AI tools (motchi.art, Lottie Tools) assist
Phase 14: Receipt Intelligence (NOT STARTED)
Goal: Extract maximum value from receipt photos. Receipts contain rich data beyond survey codes — items purchased, prices, timestamps, store numbers, payment methods.
Owner: Dev agent + Lucas (product decisions)
Repos: mcd-tracker-api
Depends on: Phase 5b (OCR — deferred, currently manual code entry)
Scope:
- OCR pipeline: Tesseract (self-hosted) or Cloud Vision (API) — decision deferred to this phase
- Receipt parsing: extract line items, total, tax, store number, timestamp, payment method
- Insights: most ordered items, average spend per visit, spending by location, visit frequency patterns
- Optimal timing: when are BOGO codes most likely to have open slots at each location?
- Data model:
ReceiptItemtable (receipt_id FK, item_name, price, quantity) - Privacy-first: all data stays on our infra, user can delete anytime
- Feed gamification: "You've tried 23 different menu items" or "Your most ordered: Big Mac (8x)"
Key Files
| Phase | File | Repo | Change |
|---|---|---|---|
| 1 | terraform/k3s.tfvars | pal-e-services | Add mcd-tracker to var.services |
| 1 | overlays/mcd-tracker/prod/* | pal-e-deployments | New kustomize overlay |
| 2 | src/mcd_tracker_api/* | mcd-tracker-api | FastAPI scaffold |
| 3 | src/mcd_tracker_api/models.py | mcd-tracker-api | Location + CouponUsage models |
| 3 | overlays/mcd-tracker/prod/postgres.yaml | pal-e-deployments | Postgres sidecar |
| 4 | src/mcd_tracker_api/auth.py | mcd-tracker-api | Keycloak JWT validation |
| 5 | src/mcd_tracker_api/routes/* | mcd-tracker-api | All API routes |
| 5 | tests/* | mcd-tracker-api | Integration tests |
| 8 | com.woodpecker.agent.plist | MacBook Air M1 | launchd agent config |
| 9 | mcd-tracker-ios/* | mcd-tracker-ios | SwiftUI app |
| 10 | fastlane/* | mcd-tracker-ios | Fastlane config |
Verification
- [ ] Phase 1:
kubectl get ns mcd-trackerexists, ArgoCD app visible - [ ] Phase 2:
curl https://mcd-tracker.tail5b443a.ts.net/healthzreturns 200 - [ ] Phase 3: Health endpoint shows DB connected, Alembic migration applied
- [ ] Phase 4: Unauthenticated request returns 401, authenticated returns 200
- [ ] Phase 5: Log 6 codes at one location → 6th rejected (limit 5). Wait 30 days (or mock time) → slot reopens. All integration tests green.
- [ ] Phase 6: Lucas approves all screens on phone
- [ ] Phase 7: Web app live, login works, can log and redeem codes
- [ ] Phase 8: Mac agent visible in Woodpecker UI, test pipeline completes
- [ ] Phase 9: iOS app builds on Mac CI, runs on physical iPhone
- [ ] Phase 10: App live on App Store, pipeline: push → TestFlight in <10 min
Related
project-mcd-tracker— project pageservice-onboarding-sop— Phase 1 procedureconvention-kustomize-overlay— Phase 1 overlay patternsop-frontend-experiment— Phase 6 playground workflowplan-pal-e-platform— platform this project runs onplan-pal-e-agency— SOPs and conventions this project follows