Requirements traceability · PRD set → status

Monetization — Requirements Traceability & Status

DONE shipped in code/PR PARTIAL started / foundation only NOT STARTED N/A doc/product artifact
The one thing to understand

The engineering stack is integrated through backend #522 (+ #523/#524), admin #168, and iOS #317. It remains observe-only / additive / flag-gated. The offer-ladder dismissal correction is pushed separately as c6305160 and awaits merge. Remaining work is product sign-off, App Store & RevenueCat configuration, analytics, sandbox QA, build upload, and the release ramp.

Overview & progress by area

Backend entitlement & enforcement~95%
iOS billing contract & Phase 4~90%
Product decisions / sign-offs~40%
App Store & RevenueCat config~15%
Trial & offer ladder (code)~75%
Grandfathering (cohort + bulk ops)~90%
Nudges & lifecycle~55%
Subscription-management UX~65%
Analytics & metrics~10%
QA / store test matrix~5%

Percentages are judgment calls to show shape, not precise metrics.

The non-code work — the big picture

These are the requirement areas that are not primarily engineering. They gate go-live and are mostly owned by product / growth / ops:

AreaOwnerStatusWhere it stands
Product sign-offsProductPARTIALCore quotas confirmed; prices, discount durations, some caps still on PRD defaults.
App Store ConnectiOS/BillingNOT STARTEDBase products exist; trial intro offer, rescue products, promo/win-back, tax/banking, privacy links not configured.
RevenueCat dashboardiOS/BillingPARTIALEntitlements + default offering exist; rescue offerings, placements, paywall-UI events, Customer Center not set.
Grandfathering operationsAdmin/SupportDONEBuilt across backend #515 + admin #163: single-user grants, cohort builder→preview→bulk apply, hand-picked multi-select, and campaign audit. Hardening (two-person approval, expiry dashboard) is a follow-up.
Nudges & lifecycle copyProduct/GrowthPARTIALUsage nudges + trial-countdown banner shipped on iOS (#312); frequency caps, suppression policy, and copy ownership still product/growth work.
Analytics planData/GrowthNOT STARTEDEvent/metric list defined in PRD; not instrumented.
QA / store test matrixQANOT STARTEDSandbox purchase / trial / offer / restore matrix not run.

Phase 0 — product decisions & sign-offs (Product)

The PRD marks these as "must confirm before store config or enforcement ships."

DecisionStatusDetail
Recurring-show generation (shows/week)DONEConfirmed: Free 1, Plus-trial 1 total, Plus 2, Pro 5. Encoded & test-locked.
One-time episode generation (episodes/week)DONEConfirmed: Free 2/week, Plus-trial 4 total in trial, Plus 10/week, Pro 50/week.
Free Explore playbackDONEConfirmed: 45 min/week, rolls one week (cap 90).
Standard Plus/Pro pricesPENDINGWeekly/monthly/yearly prices need sign-off before store config.
50% / 65% discount durationPENDINGFirst-period vs multi-period; cadence shown (monthly recommended).
One-time + file rollover capsDEFAULTEDOne-time weekly caps default to 2x base (Free 4 / Plus 20 / Pro 100); files Plus 6 / Pro 50 — confirm.
Pro files normalization (25/wk @25MB)DEFAULTEDDerived from the ambiguous "14-5 minutes" spoken requirement — confirm.
Grandfather cohort / permanent-access policyPENDINGCutoff, plan, dates, revocable?, exclusions.
Nudge frequency caps & copy ownershipPENDINGPer-session / per-72h caps; who writes copy.
Storefront availability / localized pricingPENDINGWhich storefronts; localization strategy.

How the defaults are safe: all numbers live in a versioned plan_versions DB table (seeded from code), so a sign-off change is a data edit, not a redeploy — and later an admin-dashboard edit.

App Store Connect configuration (iOS/Billing)

ItemStatusDetail
Paid Apps Agreement, tax/bankingNOT STARTEDPrerequisite for any paid IAP.
Single subscription group (Plus/Pro)CHECK6 products exist (app.lissin.audio.{plus,pro}.{weekly,monthly,yearly}); verify one group + Pro>Plus service level.
Localized names/descriptions/pricesNOT STARTEDPlus review screenshots.
3-day Free introductory offer (Plus)NOT STARTEDBackend already projects period_type=TRIAL; the App Store intro offer itself must be created.
50% / 65% rescue products/offersNOT STARTEDReal intro-priced products for the rescue ladder.
Promotional / win-back offersNOT STARTEDFor existing/lapsed subscribers.
Privacy Policy / Terms linksNOT STARTEDRequired on subscription surfaces.

RevenueCat configuration (iOS/Billing)

ItemStatusDetail
App, SDK key, ASC API key, IAP keyPARTIALPublic SDK key ships in the app; verify the server-side keys.
Entitlements plus / proDONEExist and are what the backend derives tier from.
Default offeringPARTIALA current offering exists; rescue offerings do not.
Offerings default / save50 / save65, win-backNOT STARTEDiOS already consumes these offering ids (#311, graceful fallback) — the user must still create them in the RC dashboard for the ladder to present real prices.
Paywalls per offering (Paywall Builder)NOT STARTEDWith eligibility-aware trial copy + renewal terms.
RevenueCat Placements (per iOS registry)NOT STARTEDonboarding, locked-episode, generation, cadence, document, remix, pro-sources, etc.
Paywall-UI webhook eventsNOT STARTEDImpressions/closes/exit-offers.
Dev vs prod webhooks + secretsPARTIALDev webhook secret set & verified; prod secret still required before go-live.
Customer Center (management)NOT STARTEDOptional branded self-service; else Apple native sheet.

Trial & offer ladder

RequirementStatusHow / what's left
3-day trial — backend projectionDONEWebhook maps TRIAL → trial phase + window; restricted plus_trial limits apply. (code)
3-day trial — App Store intro offerNOT STARTEDCreate the Free 3-day intro offer on Plus products. (store config)
3-day trial — iOS UIDONETrial countdown banner on home + usage nudges shipped on iOS #312; renders on live balances. (code)
Offer state machine (standard→50→65)DONEiOS #311 rescue ladder: standard→50%→65% downsell on dismiss; persists/resumes; resets on purchase; commits stage only after the offering loads; re-entrancy guarded (13 tests). (code)
Exit-offer presentationPARTIALCore ladder is merged. Dismissal-routing fix c6305160 is pushed but awaits merge; exact RC save50/save65 offerings and sandbox verification are still required.

Admin & grandfathering (operations) (Admin/Support)

RequirementStatusHow / what's left
Single-user grant / revoke / history / attributionDONEOn PR #163 → dev (mergeable); grant overlays store access, never lowers a paid tier; panel now shows phase/trial/balances. (code)
Grant-as-overlay + downgrade guardDONEBackend: a grant only applies if ≥ store tier; expiry reveals underlying tier. (code)
Cohort builder + filtersDONEbackend #515 + admin #163: cohort builder with allow-listed filters (no raw SQL), builder→preview→two-click apply in the dashboard. (code)
Hand-picked user selectionDONE#515 hand-picked user-ID preview/apply; admin Users page multi-select → "Grandfather selected (N)". (code)
Dry-run preview (counts, conflicts)DONECohort preview returns counts before apply; overlay-safe against existing grants. (code)
Bulk batch apply (idempotent, bounded)DONE#515 idempotent bulk apply, bounded (cap 1000 / hard 5000), overlay-safe; 29/29 tests. (code)
Audit viewDONECampaign audit list + per-campaign drill-down (#515) surfaced as an admin grandfathering audit page + nav (#163). (code)
Two-person approval + expiry dashboardNOT STARTEDGrandfather-hardening follow-up: reauth/approver token above a threshold, and an expiry-timeline dashboard.
Backend-enforced billing rolesPARTIALAdmin grandfathering UI + endpoints gated on billing.manage; admin-security review comments fixed. Broader per-permission enforcement on all billing routes still hardening.

In-app nudges & lifecycle (Product/Growth)

RequirementStatusDetail
Jump-notification surfacesDONEHome usage nudges + trial-countdown banner shipped on iOS #312, driven by live meter balances. (code)
Eligible momentsPARTIALUsage-approaching-limit and trial-ending moments fire on home (#312); rollover-credit / locked-feature / campaign-offer moments not yet surfaced.
Frequency caps & suppressionNOT STARTED1 nudge/session, 1 push/72h, none within 24h of dismissal; suppress during playback/creation/onboarding — policy/caps not yet implemented.
Server-side exposure persistenceNOT STARTEDCross-device frequency caps (needs an exposure table + decision API).

Subscription management UX (iOS IPRD) (iOS)

RequirementStatusHow / what's left
Plan & Billing destinationDONEiOS #312 rebuilds Plan & Billing on the live-balance access contract (phase, trial, per-meter balances). (code)
State matrix (trial/paid/canceled-active/grace/grandfathered/pending-change)PARTIALTrial / paid / grandfathered states render from the live contract; grace / canceled-active / pending-change still need per-state screens (and backend pending-change fields).
Native manage-subscriptions sheetNOT STARTEDApple sheet (or RevenueCat Customer Center) for change/cancel.
Upgrade/downgrade timing + pending-changeNOT STARTEDBackend must add pending-product/effective-date fields; iOS shows "Pro until [date], then Plus".
Restore / nothing-to-restore / offline / delayed-activationPARTIALRestore + server-authoritative refresh exist; full edge-state polish pending.

Analytics & success metrics (Data/Growth)

RequirementStatusDetail
Events (gate/paywall/purchase/trial/quota/nudge/grant)NOT STARTEDDefined in PRD; not emitted. Some playback analytics already exist via PostHog.
Dimensions (placement, offer stage, tier, phase, quota, cohort…)NOT STARTEDNeeds consistent tagging across surfaces.
Primary metrics (paid conv, trial→paid, LTV, mismatch rate…)NOT STARTEDDashboards to be built.
Observe-only usage dataDONE#514 immutable usage ledger meters generation / upload / Explore-playback / SSE and records "would-deny" logs to reconcile before enforcement is flipped on. (code)

Store test matrix & QA (QA)

RequirementStatusDetail
Sandbox trial start / ineligible standard termsNOT STARTEDNeeds the App Store intro offer configured first.
Standard→50→65 exit flowsPARTIALiOS ladder unit-tested (13 tests, #311); end-to-end sandbox run needs the RC save50/save65 offerings configured first.
Convert / cancel / expire / billing issueNOT STARTEDLifecycle matrix.
Upgrade / downgrade / restore / transfer / refundPARTIALBackend handles TRANSFER + expiry + downgrade guard (unit-tested); full end-to-end sandbox not run.
Dev/TestFlight never projects to prodPARTIALEnv now recorded per event; verify separation in dashboards.
Webhook replay / out-of-order / idempotencyDONEDedup by provider event id + stale-event ordering guard, unit-tested. (code)

Engineering (code) — what's done (Backend/iOS/Admin)

The original six-PR foundation and the nine creator-feature workstreams are now integrated through backend #522 (+ #523/#524), admin #168, and iOS #317. They remain observe-only / additive / flag-gated. Historical validation recorded 356 iOS tests passing on Xcode 26.5, 29/29 backend billing+grandfather tests, and clean admin builds. Dismissal fix c6305160 awaits merge and fresh Xcode/sandbox validation.

Enforcement is real but dark: backend metering + 402s cover generation / upload / Explore-playback / SSE behind BILLING_ENFORCE[_DOMAIN] (default OFF, fail-open); iOS gates run through a fail-open EntitlementGate. Not wired: pro_sources (intentional — the feature doesn't exist yet), and grandfather hardening (two-person approval, expiry dashboard) is a follow-up.

Rollout phases (PRD doc 07)

PhaseStatusNote
0 · Product lockPARTIALCore numbers confirmed; prices/policy pending.
1 · Foundation merge + envDONEIntegrated through backend #522 (+ #523/#524), admin #168, and iOS #317. Offer dismissal follow-up c6305160 remains a release prerequisite.
2 · Store & RevenueCat catalogNOT STARTEDThe largest remaining non-code block (user-owned).
3 · Backend DB, quota, enforcementDONEShipped #514/#515 (enforcement behind flags, default OFF).
4 · iOS placements & managementDONEGates, offer-stage ladder, trial UI, nudges, and live-balance Plan & Billing all shipped (#309/#311/#312); RC placement config lives in phase 2.
5 · Admin grandfathering & opsDONESingle-user + cohort + hand-picked + bulk + audit shipped (#515/#163); hardening (approval/expiry) a follow-up.
6 · QA & release rampNOT STARTEDAfter store config exists.
7 · ExperimentationNOT STARTEDPost-stability.
Go-live blockers (PRD)

Prod webhook secret verified with a real event · backend usage/denials authoritative (not placeholders) · trial/price/savings/renewal match the App Store sheet · grandfather cohort applied & audited · restore/refund/expiry/transfer tested · kill switches + incident playbook ready · no legacy tier consumer contradicts canonical access. Most of these are store-config + QA + ops, not code.

What's left, prioritized

The engineering is done; what remains is mostly the user's / non-code work — merging, store & RevenueCat setup, sign-offs, and the QA/enforcement ramp.

  1. Merge the 6 PRs — iOS order #309→#311→#312; backend one at a time (no concurrency guard). User
  2. App Store Connect + RevenueCat catalog — create the default/save50/save65 offerings, the 3-day trial intro offer, placements, paywalls, and the prod webhook. The biggest non-code block and the gate for real purchases. User / iOS-Billing
  3. Product price & discount sign-offs (standard prices, 50% / 65% durations, remaining caps, cohort/permanent policy, nudge caps). Product
  4. Flip enforcement flags → QA → cohort → % ramp — reconcile "would-deny" logs, run the sandbox/TestFlight matrix, then ramp BILLING_ENFORCE by domain. User / QA
  5. Analytics instrumentation + dashboards (events, dimensions, conversion/trial/LTV metrics). Data
  6. Non-blocking follow-ups — auto-present paywall on live 402; a DEBUG -billingScenario flag for screenshots; wire pro_sources when that feature exists; grandfather hardening (two-person approval, expiry dashboard). Eng (later)

Sources: lo/lissin-monetization-plan/ (01 master PRD, 03 RevenueCat/App-Store, 04 backend/db, 05 iOS paywalls, 06 admin/grandfathering, 07 rollout, 08 iOS subscription-mgmt IPRD). Code status from PRs #514 & #515 (backend→dev), #163 (admin→dev), and #309 / #311 / #312 (iOS→develop).