Onset Build Plan: Stack Value, Experiments, Paywall, Clinic¶
Status: Gates 1–5 product DoD complete. Gate 5 business DoD open (pitches + pilot).
Source: PRODUCT_REVIEW + money-saved calculator (Instacart-style).
Rule: Build only the current gate. Experiments stay Release-off until counsel clears copy.
North star¶
Onset stops selling "nicer reminders" and sells two unavoidable answers:
- Consumer: "Am I wasting the money I already spend on my stack?"
- Clinic: "Are my patients taking what I prescribed?"
Gamification = dollars and streaks tied to those answers. No XP, levels, or leaderboards.
Locked product decisions¶
| Decision | Choice |
|---|---|
| Money metric language | "Stack cost", "Taken", "Missed", "Stack value recovered". Never "health ROI" or outcome claims. |
| Free vs Pro | Soft free stays: 10 items, 7-day trends, Stack Cost basics. 3 free bottle scans/month via BottleScanUsageTracker + FeatureGate. Pro = unlimited + Experiments + 30-day + inventory alerts + Watch/widgets. |
| Docs sync | .cursorrules and README match soft free path (Gate 2). |
| Experiments copy | Counsel review before App Store release. Ship behind FeatureFlags until cleared. |
| Freeze list | No new work on Protocol Lab sims, cycle simulator, public marketplace, fitness, calendar export, performance prep. Keep code; stop expanding. |
| Clinic | Activate existing $49/mo SaaS. Patient free via clinic. Patient Pro = Intelligence Bundle. |
Gates (earned, not calendar)¶
Each gate unlocks exactly one next thing on its Definition of Done.
Gate 1 : Stack Value (money calculator) : DONE¶
Goal: Every priced stack shows monthly cost, taken, missed, and all-time recovered.
DoD:
- [x] bottlePrice + related fields on UserProtocolItem (lightweight migration)
- [x] StackCostEngine unit-tested for daily/monthly/taken/missed math
- [x] Price entry in Protocol editor + Add prices sheet + scan review
- [x] Today card: Stack Cost (empty state asks for prices)
- [x] Savings screen (More): month + all-time + year projection
- [x] Copy audit: no health claims, no em-dashes
- [x] Analytics: stackPriceAdded, stackCostCardViewed, savingsScreenViewed
Out of scope: weekly push, share cards, paywall rewrite, experiments.
Gate 2 : Paywall honesty + aha path¶
DoD:
- [x] Scanner uses FeatureGate 3 free scans/month via BottleScanUsageTracker
- [x] Stack Cost visible on OnsetExplicitPaywall when priced
- [x] .cursorrules + README agree on free vs Pro
- [x] Soft free path chosen in writing (no trial for now): Continue with free access + 3 scans/month + Stack Cost. Pro unlocks unlimited scans / 30-day / inventory alerts / Watch / sync.
- [x] Ops note: RevenueCat → Meta Ads: enable in RevenueCat dashboard (Integrations → Meta) and map to Meta app ID before paid UA. Not a code change.
- [x] Paywall copy: stack monthly cost + Pro annual ~$4/mo framing
Gate 3 : Experiments v1 (counsel-gated)¶
Entry: Gate 2 done + written counsel OK on result-card copy (or ship DEBUG/Staging only).
DoD:
- [x] StackExperiment model: itemId, metric, baselineStart/End, activeStart, durationDays, status
- [x] Metrics v1: Daily Journal energy + sleep quality (2 max; Health sleep duration deferred)
- [x] Result card: before avg, during avg, adherence %, dates. No "improved" / "worked"
- [x] Pro-gated. Free sees locked teaser on Stack Value when flag is on
- [x] Flag: stackExperiments (ON in Debug/Staging, OFF in Release until counsel)
Gate 4 : Retention loops on real value¶
DoD:
- [x] Sunday local notification: Weekly Stack Report (cost + taken + missed + streak)
- [x] One streak freeze per week
- [x] Shareable monthly receipt card (extend ProtocolShareCard pattern via StackValueShareCard)
- [x] Milestone: "Experiment window complete" local notification (only if Gate 3 flag ON)
Gate 5 : Clinic activation (mostly sales + thin product)¶
DoD (product):
- [x] Clinic-facing adherence export / summary that includes % doses taken (dose-weighted fields on shared_adherence upsert)
- [x] Drop-off signal: patient silent ≥7 days → is_silent + days_since_last_completion on share payload (clinic web surfaces badge)
- [x] Pitch kit: feed/06 + one-pager in ~/Downloads/onset-marketing-kit/pitch/ (pointer in docs/clinic/PITCH_KIT.md)
DoD (business, not code): 10 clinics pitched, 1 pilot scheduled.
Gate 1 data model¶
UserProtocolItem (new optional fields)¶
/// Bottle purchase price in the user's currency (USD default). Nil = unpriced.
var bottlePrice: Double?
/// ISO 4217, default "USD"
var bottlePriceCurrency: String?
/// When the user last set/confirmed the price
var bottlePriceUpdatedAt: Date?
Reuse existing: servingsPerContainer, currentStock, frequency, lastRestocked.
UserPreferences (optional, Gate 1b if time)¶
/// 0...1 estimated miss rate before Onset. Nil = don't show "recovered vs before" line.
var preOnsetMissRate: Double?
StackCostEngine (pure functions, no SwiftUI)¶
Inputs: active items with prices, completions in range, calendar month.
| Output | Formula |
|---|---|
costPerDose(item) |
bottlePrice / servingsPerContainer (require both > 0) |
dailyCost(item) |
costPerDose * servingsPerDay(frequency) |
monthlyStackCost |
sum of dailyCost × daysInMonth (or 30.44 avg for projection) |
takenValue(range) |
sum over completions (non-skipped) of costPerDose |
scheduledValue(range) |
sum over active days of dailyCost for items that were active |
missedValue(range) |
max(0, scheduledValue - takenValue) |
recoveredAllTime |
sum of takenValue since first priced completion |
yearProjection |
monthlyStackCost × 12 |
Empty / partial rules: - If zero items priced: show empty state, do not invent defaults on Today. - Optional later: category defaults in editor only as placeholders user must confirm. - Skipped completions count as neither taken nor (for v1) missed; missed = scheduled and not completed and not skipped by end of day. For v1 simplicity: missed = scheduledValue − takenValue for past days only (today counts taken only).
Gate 1 UI copy (paste-ready)¶
Today card : empty¶
Today card : populated¶
Savings screen : header¶
Savings : month section¶
Savings : all time¶
Savings : year line¶
Savings : footer¶
General wellness organizer. Dollar amounts are based on prices you enter and doses you log. Not financial or medical advice.
Protocol / Inventory price field¶
Bottle price
What you paid for this bottle (optional)
$ [ 24.99 ]
120 servings · about $0.21 per dose
Paywall line (Gate 2)¶
Weekly report push (Gate 4)¶
Share card (Gate 4)¶
Taboo on these surfaces¶
No: optimize, improve, boost, ROI on health, "saved your body", treat, cure, you should.
Experiments result card copy (Gate 3, counsel before Release)¶
EXPERIMENT RESULT
Magnesium Glycinate · 30 days
Metric: Sleep (Apple Health)
Before (14 days): average 6h 52m
During (30 days): average 7h 21m
Adherence during: 93%
Your logged numbers side by side. Onset does not interpret results or give medical advice.
Never: "improved", "worked", "effective", "increased your sleep by".
Analytics events (add to ProductAnalytics)¶
| Event | When |
|---|---|
stackPriceAdded |
User saves a bottle price |
stackCostCardViewed |
Today card appears with priced data |
savingsScreenViewed |
Savings screen open |
stackCostEmptyCtaTapped |
Empty-state Add prices |
experimentStarted |
Gate 3 |
experimentCompleted |
Gate 3 |
experimentScreenViewed |
Gate 3 |
weeklyStackReportOpened |
Gate 4 |
streakFreezeUsed |
Gate 4 |
stackValueShared |
Gate 4 |
clinicAdherenceShared |
Gate 5 |
File touch list (Gate 1)¶
| Area | Files |
|---|---|
| Model | Onset/Core/Models/UserProtocolItem.swift |
| Engine | New Onset/Core/Engine/StackCostEngine.swift |
| Tests | New OnsetTests/StackCostEngineTests.swift |
| Editor | ProtocolItemEditor.swift, SupplementDetailView.swift inventory section |
| Inventory | InventoryView.swift |
| Today | TodayView.swift or small StackCostCard.swift |
| Savings | New Features/Savings/SavingsView.swift |
| Nav | MoreView.swift row |
| Analytics | ProductAnalytics.swift |
Sell track (parallel, not blocked by Gate 1 code)¶
| Week | Action |
|---|---|
| Now | Pitch 10 clinics with ~/Downloads/onset-marketing-kit/instagram/feed/06-clinic-protocol.png |
| Gate 2 | Insert Affiliate: 10 creators, Reel scripts from ADS_PLAYBOOK |
| Gate 3+ | $30/day Meta test only after Stack Cost is in a TestFlight users can feel |
Success metrics¶
| Metric | Target |
|---|---|
| % new users who price ≥1 item in 48h | ≥40% |
| Paywall→purchase after Gate 2 | ≥4% |
| D30 retention: Experiment starters vs not | ≥2× |
| Paying clinic pilots | 1 in 45 days |
Implementation order inside Gate 1¶
- Schema fields +
StackCostEngine+ tests - Price field in editor / inventory
- Today card
- Savings screen + More entry
- Analytics + copy pass
Start at step 1 next.