Plan: mcd-tracker

plan-mcd-tracker Plan

plan deprecated

Plan: mcd-tracker

Vision

McDonald's BOGO coupon tracker — track survey codes across locations, know how many slots remain, see when slots reopen (rolling 30-day window). Multi-user from day one. Enterprise-grade: Keycloak auth, CI/CD pipelines, App Store distribution.
Meta-vision: Full-lifecycle DORA Elite proof. Validate the pal-e-platform dev experience end-to-end: devops → backend → frontend → mobile. Every SOP exercised, every pipeline stage proven, every architectural pattern stress-tested. The app is the vehicle; the dev process is the deliverable.

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

First greenfield project built entirely on the hardened pal-e-platform. Previous projects (basketball-api, westside-app, pal-e-docs) were onboarded incrementally as the platform matured. mcd-tracker is the first to follow every SOP from project creation through App Store submission.
What's already done:
  • [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

None. First plan for this project.

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)

Goal: Production namespace, Harbor project, ArgoCD app, and kustomize overlay ready — before any code exists.
Owner: Dev agent
Repos: forgejo_admin/pal-e-services, forgejo_admin/pal-e-deployments
Forgejo Issue: TBD
Steps:
  • Add mcd-tracker entry to pal-e-services/terraform/k3s.tfvars var.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/
Deliverables: pending

Phase 2: Backend Scaffold + First Deploy (COMPLETED)

Goal: FastAPI skeleton running in prod — health endpoint responding, CI green, ArgoCD syncing.
Owner: Dev agent
Repo: forgejo_admin/mcd-tracker-api
Forgejo Issue: TBD
Steps:
  • Create mcd-tracker-api repo 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
Deliverables: pending

Phase 3: Data Model + Postgres (COMPLETED)

Goal: Postgres sidecar deployed, SQLAlchemy models defined, Alembic migrations running, DB-backed health check.
Owner: Dev agent
Repos: forgejo_admin/mcd-tracker-api, forgejo_admin/pal-e-deployments
Forgejo Issue: TBD
Steps:
  • 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
Data Model Notes:
  • No separate User table — user identity comes from Keycloak JWT (sub claim). Store keycloak_sub as 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 days from the 5 active usages at a location
Deliverables: pending

Phase 4: Keycloak Realm + Auth (COMPLETED)

Goal: Dedicated Keycloak realm with user/admin roles, JWT validation in API, protected endpoints.
Owner: Dev agent + manual Keycloak admin
Repos: forgejo_admin/mcd-tracker-api
Forgejo Issue: TBD
Steps:
  • Create mcd-tracker realm in Keycloak admin console
  • Create realm roles: user, admin
  • Create OIDC client: mcd-tracker-app (for SvelteKit) and mcd-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
Deliverables: pending

Phase 5: Core API Endpoints + Integration Tests (COMPLETED)

Goal: Full CRUD API with rolling window logic, integration tests, deployed to prod.
Owner: Dev agent
Repo: forgejo_admin/mcd-tracker-api
Forgejo Issue: TBD
Steps:
  • Routes:
  • Rolling window logic:
  • Integration tests (pytest, real Postgres):
  • Deploy and verify via curl
Deliverables: pending

Phase 6: Frontend Playground (IN PROGRESS — CSS consolidated, @-comment specs added, pending phone approval)

Goal: HTML/CSS mockups of all screens, verified on phone, approved by Lucas.
Owner: Main session (Lucas iterates directly)
Repo: forgejo_admin/mcd-tracker-playground
Forgejo Issue: mcd-tracker-playground #4 (closed)
Steps:
  • Decide: folder experiment in html-playground or 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
Deliverables: PR #5 merged (mcd-tracker-playground #4): consolidated ~1,000 lines of inline CSS into app.css, added @-comment integration specs to all 12 HTML files. SOP: 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)

Goal: Production SvelteKit web app promoted from playground, deployed to prod with Keycloak auth.
Owner: Dev agent
Repos: forgejo_admin/mcd-tracker-app, forgejo_admin/pal-e-services, forgejo_admin/pal-e-deployments
Forgejo Issue: TBD
Steps:
  • Create mcd-tracker-app repo 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-app to k3s.tfvars + kustomize overlay
  • Deploy, verify on phone
Deliverables: pending

Phase 8: Mac Woodpecker Agent (NOT STARTED)

Goal: MacBook Air M1 running as a Woodpecker CI agent with local backend, capable of executing xcodebuild.
Owner: Main session (manual Mac setup)
Repo: forgejo_admin/pal-e-platform (agent config docs)
Forgejo Issue: TBD
Steps:
  • Install Xcode on MacBook Air M1 (App Store)
  • Install Woodpecker agent binary (brew install woodpecker-ci/tap/woodpecker-agent or 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
Deliverables: pending

Phase 9: Capacitor iOS Build + Native Plugins (NOT STARTED — replaces Swift app)

Goal: SwiftUI app with same screens as SvelteKit, Keycloak OIDC auth, building on Mac CI agent.
Owner: Dev agent (code on archbox) + Mac agent (builds)
Repo: forgejo_admin/mcd-tracker-ios
Forgejo Issue: TBD
Steps:
  • Create mcd-tracker-ios repo on Forgejo
  • SwiftUI app scaffold:
  • Keycloak OIDC auth via ASWebAuthenticationSession (public client + PKCE — mcd-tracker-ios client created in Phase 4)
  • API client layer — same endpoints as SvelteKit, JWT Bearer auth
  • .woodpecker.yaml with labels: [platform: darwin] — targets Mac agent
  • Pipeline: xcodebuild testxcodebuild archive → export .ipa
Deliverables: pending

Phase 10: App Store Submission (NOT STARTED)

Goal: mcd-tracker live on the App Store. Full CI/CD from git push to TestFlight to production.
Owner: Main session + Dev agent
Repo: forgejo_admin/mcd-tracker-ios
Forgejo Issue: TBD
Steps:
  • Enroll in Apple Developer Program ($99/yr)
  • Fastlane setup:
  • CI pipeline addition: after xcodebuild archive, run fastlane 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)
Deliverables: pending

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 /events endpoint 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 /stats endpoint — 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: ReceiptItem table (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-tracker exists, ArgoCD app visible
  • [ ] Phase 2: curl https://mcd-tracker.tail5b443a.ts.net/healthz returns 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

QA nits and deferred work will be tracked here as phases complete.
  • project-mcd-tracker — project page
  • service-onboarding-sop — Phase 1 procedure
  • convention-kustomize-overlay — Phase 1 overlay pattern
  • sop-frontend-experiment — Phase 6 playground workflow
  • plan-pal-e-platform — platform this project runs on
  • plan-pal-e-agency — SOPs and conventions this project follows