Flow Summary
SwiftCause handles donations through three primary channels. Since 30 March, significant code has shipped: Magic Link token infrastructure (PR #622), Stripe Tap-to-Pay on Android (PR #619), server-side donation exports (PR #623), and CSV security hardening (PR #626). The kiosk and magic link channels now work end-to-end on device (APK validated 12 Apr). The consent ledger and web portal remain the critical gaps with 27 days to GTM (9 May 2026).
What Changed Since 30 March 2026
- PR #622 Magic Link token generation, validation API, /link/[token] page, Gift Aid declaration form — SHIPPED (9 Apr)
- PR #619 Stripe Tap-to-Pay on Android via Stripe SDK — SHIPPED (9 Apr)
- PR #623 Server-side donation export (all donations, not just visible page) — SHIPPED (10 Apr)
- PR #625 Donation export documentation — SHIPPED (10 Apr)
- PR #626 CSV formula injection fix for HMRC & internal exports — SHIPPED (10 Apr)
- PR #620 Gift Aid flow documentation — SHIPPED (9 Apr)
- PR #618 Skeleton loading states — SHIPPED (8 Apr)
- PR #617 Admin panel pagination — SHIPPED (7 Apr)
- PR #615 Webhook event handling improvements — SHIPPED (3 Apr)
- APK First Android alpha build — full donation flow validated on device (12 Apr)
- #627 Configurable post-donation redirect timer — ISSUE CREATED (pending PM agreement)
Flow 1: Kiosk Tap-to-Pay
Shipped
Stripe Terminal + Tap-to-Pay on Android (PR #619). Full flow validated on APK: campaign list → amount → payment → QR → Gift Aid. Thank You screen with QR code + countdown working.
Flow 2: QR / Magic Link
Shipped
PR #622 merged. Token generation (SHA-256), /link/[token] validation page, Gift Aid declaration form on donor's phone. APK recording confirms QR → phone flow works end-to-end.
Flow 3: Web Portal
Not Built
Direct URL to /donate/[campaignId] still missing. Shares components with Flow 2 but needs its own entry point, Payment Element integration, and checkout page.
Flow 4: Recurring Donations
Partial
Stripe subscriptions work on kiosk. But recurring + Gift Aid declaration linking still unhandled. Only possible via Payment Element (web portal).
Flow 5: Returning Donor
Partial
Email lookup + auto-fill exists. But auto-submits consent without re-confirming — legal risk. Needs confirmation screen. No change since 30 Mar.
Admin Flows
Partial
Server-side donation export (PR #623) + CSV security (PR #626) + admin pagination (PR #617) shipped. But no Gift Aid dashboard, HMRC claim batch UI, or flow configuration screens yet.
Newly shipped (since 30 Mar)
Changed — needs modification
Gap — blocking compliance
Possible Outcomes per Flow
| Flow | Gift Aid Eligible? | GASDS Eligible? | Recurring Possible? | Donor Identity? |
| Kiosk Tap-to-Pay (Option A) |
Yes — on-screen form |
Yes — ≤£30 anonymous |
No — Terminal can't store PM |
Only if Option A completed |
| Kiosk Tap-to-Pay (Option B+) |
Via QR follow-up — NOW WORKING |
Yes — ≤£30 anonymous |
No |
Via magic link on phone |
| QR / Magic Link Checkout |
Yes — form on donor's phone |
Yes — if ≤£30 |
Yes — Payment Element stores PM |
Yes — wallet pre-fill + form |
| Web Portal |
Yes — inline form |
Yes — if ≤£30 |
Yes — Payment Element stores PM |
Yes — wallet pre-fill + form |
| Recurring (any Payment Element flow) |
Yes — one declaration covers all |
Yes — per-invoice if ≤£30 |
Yes — built-in |
Yes |
GTM Readiness: 27 Days to Launch (9 May 2026)
| 10-Step Donor Journey | Status | Issue/PR | Notes |
| 1. Kiosk displays campaign | SHIPPED | — | Working on Android APK |
| 2. Donor taps to pay | SHIPPED | PR #619 | Stripe Tap-to-Pay on Android |
| 3. Webhook processes payment | SHIPPED | PR #615 | Idempotency + atomicity fixed |
| 4. Magic link generated | SHIPPED | PR #622 | SHA-256 tokens, 30-day expiry |
| 5. Donor opens link on phone | SHIPPED | PR #622 | /link/[token] page + validation API |
| 6. Gift Aid form displayed | SHIPPED | PR #622 | Declaration form on phone, APK-validated |
| 7. Consent recorded | NOT BUILT | #609 | ConsentEvents Cloud Function — UNASSIGNED 15 days |
| 8. Donor reconciliation | NOT BUILT | #611 | Unassigned 15 days |
| 9. Recurring option | DEFERRED | #592 | Phase 3, not in go-live scope |
| 10. HMRC export | SHIPPED | PR #623, #626 | Server-side export + CSV injection fix |
Flow 1: Kiosk Tap-to-Pay
The primary high-footfall flow. Donor walks up to a kiosk (Android handset or fixed device), selects a campaign, chooses amount, and taps their card or phone. Gift Aid is captured either before payment (Option A) or after via QR (Option B+). APK build validated 12 April — full flow works end-to-end on Android device.
1Campaign List ScreenBUILT
Kiosk campaign list. Donor browses available campaigns. Working on Android APK.
↓
2Campaign Detail + Amount SelectionBUILT
Predefined amounts or custom £1–£10,000. APK shows £5/£10/£25/£50/Other.
↓
3Gift Aid Boost PanelBUILT
GiftAidBoostPanel.tsx — “Turn your £X into £Y for free”. Two CTAs: Yes / No.
↓
Donor taps “Yes, Boost”?
Yes
4aEmail GateBUILT
GiftAidPage.tsx checks for saved profile by email.
4bGift Aid Details FormBUILT
GiftAidDetailsPanel.tsx — name, address, postcode, HMRC declaration checkbox.
No
Skips Gift Aid. Goes straight to payment. Donation becomes GASDS-eligible if ≤£30.
↓
5Stripe Terminal / Tap-to-Pay PaymentSHIPPED PR #619
Stripe Tap-to-Pay on Android via Stripe SDK. Payment processing with “Processing your donation...” screen. Returns NO donor identity data.
↓
6Webhook + Token CreationSHIPPED PR #622
handlePaymentCompletedStripeWebhook. If isGiftAidCaptured: false, creates giftAidFollowUpToken with SHA-256 crypto token, donationId, 30-day expiry. Atomic batch writes. Idempotent (PR #615).
↓
7Thank You Screen with QRSHIPPED — APK VALIDATED
ThankYouScreen shows: celebration header, QR code encoding magic link URL, “I've scanned the QR code” button, auto-return countdown.
APK recording (12 Apr) confirms this works. Countdown currently 29s —
Issue #627 proposes configurable timer (pending PM agreement).
↓
8Donor Scans QR → Flow 2SHIPPED PR #622
Opens /link/[token] on donor’s phone. Validation API checks token, returns donation context. APK recording confirms full flow: QR scan → Gift Aid form → auto-return.
Gift Aid: Option A (instant) or Option B+ (via QR) — BOTH WORKING
GASDS: If ≤£30 and no Gift Aid
Identity: Via QR checkout on phone
1–5Core Kiosk FlowCOMPLETE
Campaign list → amount → Gift Aid boost → form → payment. All working on APK.
↓
6Production HardeningBLOCKER SC-B004
Firestore rules currently open for dev. Must tighten security rules, deploy indexes, configure TTL policies, deploy Cloud Functions before production use.
↓
7Consent Ledger IntegrationUNASSIGNED 15 DAYS
#609 ConsentEvents Cloud Function — computes DonorRecord.currentConsent from ConsentEvents subcollection. Not built. This is the #1 GTM risk.
↓
8IP Address CaptureUNASSIGNED #610
HMRC audit trail requires IP address on declarations. Server-side capture not implemented. 15 days unassigned.
↓
9Donor ReconciliationUNASSIGNED #611
Link magic link declarations back to donor profiles. Merge/dedup donor records. 15 days unassigned.
↓
10QR-to-Submit Funnel AnalyticsUNASSIGNED #612
Track QR scan rate, form completion rate, drop-off points. Critical for measuring magic link ROI. 15 days unassigned.
↓
11APK Cleanup for DemosBLOCKER SC-B009
Remove “Feed the devs” test campaign. Verify Stripe live key toggle. Reduce auto-return from 29s (Issue #627). Record polished demo video.
CRITICAL: 4 Sprint 04 tasks (#609–#612) unassigned 15 days. This is the #1 GTM risk.
Flow 2: QR / Magic Link Checkout
Donor scans the QR code displayed on the kiosk Thank You screen. This opens a checkout page on their own phone. This flow was entirely missing on 30 March. It now exists — PR #622 shipped the magic link token infrastructure, /link/[token] page, validation API, and Gift Aid declaration form. APK recording (12 Apr) confirms end-to-end flow on device.
1Donor Scans QR / Taps Magic LinkSHIPPED
QR on kiosk Thank You screen encodes URL: /link/[tokenId]. Opens on donor’s phone browser. APK recording confirms this works.
↓
2Token ValidationSHIPPED
GET /api/link/[token] validates token exists, not expired (<30 days), status is “pending”. Returns donation context (amount, campaign name). SHA-256 hashed storage. Deterministic doc IDs for idempotency.
↓
Post-payment follow-up (from kiosk QR)
3Gift Aid Declaration FormSHIPPED
On donor’s phone (responsive). HMRC-required fields: name, address, postcode, declaration checkbox. APK recording shows form completing successfully with auto-return to kiosk.
↓
4Declaration Created + Token ConsumedSHIPPED
POST /api/link/[token]/complete creates declaration in Firestore linked to original donationId. Sets captureMethod: POST_DONATION_QR. Marks token as “completed”. Atomic batch writes.
↓
5Confirmation + Auto-ReturnSHIPPED
Kiosk detects QR scan completion and auto-returns to campaign list after countdown. Donor sees confirmation on their phone.
Gift Aid: Full capture via phone form — WORKING
Identity: Captured via form on donor's phone
AConsentEvents Cloud Function (#609)UNASSIGNED
Each Gift Aid declaration should create a ConsentEvent in the donor's subcollection. Cloud Function computes DonorRecord.currentConsent from event history. Not built — 15 days unassigned.
↓
BIP Address Capture (#610)UNASSIGNED
HMRC audit trail requires IP address on every declaration. Server-side capture needed on the /api/link/[token]/complete endpoint. Not implemented.
↓
CDonor Reconciliation (#611)UNASSIGNED
Link magic link declarations back to donor profiles. Handle merge/dedup when same email appears across multiple donations.
↓
DQR-to-Submit Analytics (#612)UNASSIGNED
Track: QR displayed count, QR scanned count, form started, form completed, drop-off points. Essential for proving magic link ROI to charities.
↓
EFresh Checkout Path (not just post-payment)NOT BUILT
Flow 2 currently only handles post-payment follow-up (Gift Aid only, no second payment). A fresh checkout path where QR leads to full Payment Element checkout is not yet built — that's Flow 3.
↓
FConfirmation Email (GA-04)NOT BUILT
SendGrid not set up (#605, 16+ days unassigned). Cannot send Gift Aid confirmation emails until email infrastructure exists.
The core flow works. The compliance layer (consent ledger, IP capture, reconciliation) does not. These are the gating items for GTM.
Flow 3: Web Portal Checkout
Direct URL to /donate/[campaignId] — shared via email, social media, or charity website. Responsive page serving both desktop and mobile. Uses the same Payment Element + Gift Aid form components as Flow 2. This flow is still not built — no change since 30 March.
1Campaign Page (app/campaign/[id])BUILT
Public campaign detail page exists. Shows title, description, progress bar.
↓
2Payment Page (app/payment/[id])BUILT
Payment route exists BUT currently routes to kiosk-bound flow (requires kiosk context, Stripe Terminal).
↓
No web checkout capability
No Payment Element integration. No way to pay without a physical kiosk terminal. No Gift Aid capture on web. This is unchanged since 30 March.
1Campaign Landing PageEXISTS
Same public campaign page. Add “Donate Online” CTA button.
↓
2Web Checkout: Amount + Gift AidNEW BUILD
/donate/[campaignId] — responsive page. Amount selector, one-time/recurring toggle, Gift Aid boost panel, full declaration form. Can reuse components from PR #622.
↓
3Stripe Payment ElementNEW BUILD
Payment Element with requestPayerName + requestPayerEmail. Supports Apple Pay, Google Pay, card entry. Wallet pre-fills donor identity into Gift Aid form.
↓
4createWebPaymentIntentNEW BACKEND
New Cloud Function. Creates PaymentIntent with automatic_payment_methods. For recurring: creates Stripe customer + subscription.
↓
5Webhook ProcessingEXISTING
Same handlePaymentCompletedStripeWebhook (now hardened via PR #615). Reads declarationId from metadata.
↓
6Web Thank You + EmailNEW
Confirmation page with Gift Aid summary. Confirmation email (blocked by #605 SendGrid). Social sharing prompt.
Gift Aid: Full inline capture
Recurring: Yes, via Payment Element
Identity: Full (wallet + typed)
Architecture Note: Flows 2 and 3 share 90% of their codebase. The Gift Aid form, validation logic, and confirmation page from PR #622 can be reused. They differ only in entry point: Flow 2 validates a follow-up token; Flow 3 looks up a campaign by ID and adds Payment Element. Build once, route twice.
Admin Flows
Charity treasurers and SwiftCause admins need visibility into Gift Aid declarations, HMRC claim status, and GASDS tracking. Since 30 March, three key admin improvements shipped: server-side donation export (PR #623), CSV formula injection hardening (PR #626), and admin panel pagination (PR #617). But Gift Aid admin screens remain unbuilt.
1Admin DashboardBUILT
Basic overview. Campaign stats, donation totals.
2Campaign ManagementBUILT
Create/edit campaigns. giftAidEnabled toggle exists but no flow config.
3Donations ListUPDATED PR #617
View donations with pagination (PR #617). No Gift Aid status column yet. No HMRC claim tracking.
4Server-Side Donation ExportNEW PR #623
Server-side CSV export for all donations (not just visible page). Export batches + history + re-download. CSV formula injection hardened (PR #626).
5Kiosk ManagementBUILT
6User ManagementBUILT
No Gift Aid admin screens
No declaration viewer. No HMRC batch generator UI. No GASDS tracker. No flow configuration. Gift Aid CSV lib function exists but no admin UI wrapping it.
1Admin DashboardUPDATED
Add Gift Aid summary cards: total claimed, pending, GASDS pool, conversion rate.
↓
2Gift Aid Dashboard (GA-07)NEW
Declarations table with filters (status, date, campaign). Validation flags. GASDS tab.
↓
3HMRC Claim Batches (GA-08)NEW
Select period → preview → generate R68 CSV → download → mark submitted → mark paid. Server-side export infrastructure (PR #623) can be extended for this.
↓
4Flow Configuration (GA-14)NEW
Per-campaign: flow type (Option A / B+ / Hybrid), Thank You screen timeout (Issue #627), QR toggle, custom messaging.
↓
5GASDS InsightsNEW
Annual cap tracker (£8,000). 10× matching rule calculator. “Lost money” figure for donors who could convert to full Gift Aid.
↓
6Donor Management (DI-01)NEW
Donor profiles list. Declaration history per donor. Address update workflow. Depends on #611 (donor reconciliation).
HMRC Claim Submission Flow (Treasurer)
1Open HMRC Claims tab
See pending declarations count and estimated claim value.
↓
2Select claim period
Tax year filter + date range. System pre-selects declarations with hmrcClaimStatus: “pending”.
↓
3Preview & validate
Validation flags: missing postcode, invalid name, suspicious amounts. Treasurer can exclude individual records.
↓
4Generate R68 CSVPARTIALLY SHIPPED
giftAidCsv.ts exists and is HMRC-compliant. CSV formula injection hardened (PR #626). Server-side export infrastructure built (PR #623). But no admin UI for triggering Gift Aid-specific export yet.
↓
5Upload to Charities Online
Treasurer uploads CSV to HMRC Charities Online portal manually.
↓
6Mark as submitted
Updates all declarations in batch to hmrcClaimStatus: “submitted”.
↓
7Mark as paid
When HMRC payment arrives (5–6 weeks), mark paid with actual amount received.
Data Capture Matrix
What each flow captures vs what HMRC requires. Updated 12 April 2026 to reflect shipped code. The QR Follow-Up column now shows actual state from PR #622 (not aspirational).
| Data Field |
HMRC Required? |
Kiosk Option A (as-is) |
Kiosk Option B+ (as-is via QR) |
QR Follow-Up (SHIPPED PR #622) |
Web Portal (to-be) |
| First Name | Yes | Form | Via QR form | Form — SHIPPED | Form + Wallet |
| Surname | Yes | Form | Via QR form | Form — SHIPPED | Form + Wallet |
| Email | Recommended | Form | Via QR form | Form — SHIPPED | Form + Wallet |
| House Number | Recommended | Form | Via QR form | Form — SHIPPED | Form |
| Address Line 1 | Yes | Form | Via QR form | Form — SHIPPED | Form |
| Address Line 2 | Optional | Form | Via QR form | Form — SHIPPED | Form |
| Town | Yes | Form | Via QR form | Form — SHIPPED | Form |
| Postcode | Yes | Form | Via QR form | Form — SHIPPED | Form |
| Gift Aid Consent | Yes | Checkbox | Via QR form | Checkbox — SHIPPED | Checkbox |
| UK Taxpayer Confirm | Yes | Combined | Via QR form | Checkbox — SHIPPED | Checkbox |
| HMRC Declaration Text | Yes (Ch.3) | Shown | Via QR form | Shown — SHIPPED | Shown |
| Declaration Type | Yes | Missing | Missing | Needs verification | Radio |
| GDPR Consent | GDPR req. | Missing | Missing | Needs verification | Checkbox |
| Future Consent | Optional | Missing | Missing | Needs verification | Checkbox |
| Home Address Confirmed | Yes | Missing | Missing | Needs verification | Checkbox |
| IP Address | Audit trail | Missing | Missing | Missing — #610 unassigned | Server-side |
| Capture Method | Audit trail | Missing | POST_DONATION_QR | POST_DONATION_QR — SHIPPED | ON_SCREEN |
| Donation Amount | Yes (pence) | Auto | Auto | Auto — SHIPPED | Auto |
| Tax Year | Yes | Bug (GA-01) | Bug | Needs verification | Fixed helper |
Key Takeaway (Updated 12 Apr): The QR Follow-Up flow now captures most HMRC-required fields (PR #622 shipped). The critical remaining gaps are: IP address capture (#610, unassigned), and verification needed on whether declarationType, GDPR consent, and homeAddressConfirmed fields are included in the PR #622 form. The consent ledger (#609) is the compliance gating item — without it, declarations exist but aren't properly tracked in the donor record.
APK Build Review — 12 April 2026
Jitesh Singh delivered the first Android APK (app-debug.apk, 70.1 MB) to #charity_project at 10:35 BST. This review is based on PM screen recording analysis of the complete donation flow. Kiosk credentials: ID hz0vffcVAQELv4NoAo5I, access code 12345.
Overall Verdict: PASS — The core donation flow works end-to-end on Android device. This is the first time kiosk → payment → QR → Gift Aid has been validated on real hardware. Ready for internal alpha testing with cleanup items noted below.
7-Step Flow Walkthrough
Each step observed in the screen recording, mapped to the donor journey.
Step 2
Campaign List
Pass
Step 3
Amount Select
Pass
Step 5
QR + Thank You
Pass
Step 6
Gift Aid Form
Pass
Step 7
Auto-Return
Timer Issue
Detailed Step Analysis
1Kiosk Login ScreenPASS
SwiftCause branded splash. Kiosk ID + access code entry. Clean UI, responsive input fields. Successfully authenticates to Firebase.
↓
2Campaign ListPASS
Displays campaigns from Firebase. Card layout with campaign images, titles, progress bars. Includes test campaign “Feed the devs” (needs removal before demo). Skeleton loaders working (PR #618).
↓
3Campaign Detail + Amount SelectionPASS
Campaign description with rich text rendering. Amount presets: £5, £10, £25, £50, Other (custom). Sticky amount panel at bottom. “Donate Now” CTA prominent. Amount validation working.
↓
4Stripe Tap-to-Pay PaymentPASS
“Processing your donation...” screen shown during Stripe Terminal interaction. Payment simulation completes successfully. Spinner animation smooth. Transitions cleanly to Thank You screen.
5Thank You Screen with QR CodePASS
Celebration header: “Thank you for your donation!” QR code rendered clearly at centre of screen. Encodes magic link URL (/link/[token]). “I've scanned the QR code” button below QR. Auto-return countdown visible at bottom.
↓
6Gift Aid Declaration Form (on phone)PASS
Donor scans QR with phone camera. /link/[token] page loads. Token validated via API. Gift Aid form displays with: first name, surname, email, house number, address, town, postcode, declaration checkbox. Form submits successfully → declaration created in Firestore.
↓
7Auto-Return to Campaign ListTIMER WARNING
Kiosk detects QR scan completion and shows countdown. Auto-returns to campaign list after countdown expires.
Issue: Countdown is 29 seconds — too long for a shared kiosk. Donor stands watching a timer instead of walking away.
Issue #627 proposes configurable timer (org + campaign level, 15s default). Pending PM agreement.
Findings
GOOD End-to-End Flow Works
First time the full kiosk → payment → QR → Gift Aid → return flow has been validated on real Android hardware. This is a major milestone. The donation journey from campaign selection to Gift Aid capture works without errors.
GOOD Stripe Tap-to-Pay Integration Solid
Payment processing via Stripe SDK on Android works cleanly (PR #619). Processing screen, spinner, and success transition are smooth. No payment failures observed during testing.
GOOD Magic Link QR → Phone Handoff Works
QR code on Thank You screen encodes the correct magic link URL. Phone camera scans it successfully. /link/[token] page loads, validates the token, and displays the Gift Aid form. The cross-device handoff (kiosk → phone) works as designed (PR #622).
GOOD UI Quality Strong for Alpha
Skeleton loaders (PR #618), image caching, rich text campaign descriptions, sticky amount panel. Polish is above typical alpha level. Branding consistent. Animations smooth.
WARNING Test Data Visible — “Feed the devs” Campaign
The campaign list includes a test campaign titled “Feed the devs” with casual copy. This must be removed or hidden before any external demo. Consider adding a campaign visibility flag (draft/published) or using a separate demo Firebase project. Impact: demo readiness.
WARNING 29-Second Auto-Return Countdown Too Long
After QR scan completion, the kiosk shows a 29-second countdown before returning to the campaign list. In a high-footfall charity shop, this means the kiosk is unavailable for nearly 30 seconds between donors. Recommendation: reduce to 15 seconds default, make configurable per org/campaign. Issue #627 created with 2-tier Firestore config proposal.
WARNING Payment Simulation Mode — Stripe Live Key Toggle Needed
Current build uses Stripe test mode (simulated payments). Before any production or customer demo with real cards, must verify the Stripe live/test key toggle works correctly. Need environment config that switches cleanly between test and production Stripe keys.
ACTION REQUIRED Known Issues from Jitesh
Jitesh flagged three known issues in his Slack post: (1) YouTube video playback restricted in-app, (2) auto-refresh kiosk data mechanism not implemented, (3) no secure logout mechanism (can't have a simple logout button as any donor could tap it). These are acknowledged and non-blocking for alpha testing, but items 2–3 need resolution before production.
ACTION REQUIRED Gift Aid Form Missing Compliance Fields
The Gift Aid declaration form captures core HMRC fields (name, address, postcode, declaration). However, the following compliance fields need verification against PR #622 code: declarationType (past_only / past_and_future radio), GDPR consent checkbox, future contact consent, home address confirmation checkbox, and IP address capture (#610, unassigned). Without these, declarations are usable but not fully HMRC-compliant.
APK Review Summary
| Area | Status | Notes |
| Kiosk Login | Pass | Firebase auth working |
| Campaign List | Pass | Test data needs cleanup |
| Amount Selection | Pass | Presets + custom working |
| Stripe Tap-to-Pay | Pass | Test mode — live key toggle needed |
| QR Code Generation | Pass | Magic link URL encoded correctly |
| Phone Handoff | Pass | Cross-device flow works |
| Gift Aid Form | Pass | Core fields — compliance fields need verification |
| Auto-Return Timer | Warning | 29s too long — Issue #627 created |
| Demo Readiness | Warning | Test data + payment mode need cleanup |
| Consent Ledger | Not Built | #609–#612 unassigned 15 days |
| IP Capture | Not Built | #610 unassigned |
| Production Hardening | Not Done | Firestore rules, indexes, TTL, deployment |
PM Recommendation: Acknowledge this milestone publicly in #charity_project — the team needs to see PM engagement after delivering a working end-to-end flow. Then focus on the three cleanup items (test data, timer, Stripe keys) and the four unassigned consent ledger tasks. The APK proves the architecture works; now it needs the compliance layer to be production-ready.