Phase 24 · Complete 🔒 Locked — Current Lead
Front-Door Fork Route
Diagonal duotone split screen at /start. Teal Learning Lab wedge vs. gold Creator Studio wedge with animated seam and J-monogram seal.
Spec highlights
- Route /start renders Fork A; no sidebar, no shell chrome
- Teal panel navigates to /learn; gold panel to /studio
- Seam hairline and seal position are JS-driven per frame
- prefers-reduced-motion disables all ambient animation
- Mobile: steeper seam for portrait; tap to expand then navigate
Open questions
- What happens when a signed-in user hits /start? Redirect to /app or show the fork anyway?
- Does the fork chooser replace the current landing page, or only appear at /start?
- Should returning members see their last-chosen side animate first?
Phase 31 (browse) · New
Learning Lab Browse + Filter + Search
Full-width image-heavy catalog view with type filter chips, category pills, keyword search, and results grid. No sidebar.
Spec highlights
- No sidebar on any Learning Lab route (full-width, image-heavy)
- Filter chips are multi-select; active filters show a count badge
- Search filters STDB offers table on title and description client-side
- Empty state: "No results for [query]" with clear-filter CTA
- Anonymous users can browse without signing in
Open questions
- Should search results be ranked by recency, rating, or purchase count, or alphabetical by default?
- Is there a "Featured" or "New" badge on offer cards?
- Should the offer detail page show related offers below the reviews section?
Phase 31 (detail) · New
Offer Detail Page
Full route at /learn/offers/slug with cover image, creator info, type-specific content preview, Buy Now CTA, and reviews below the fold.
Spec highlights
- Offer detail is a full route /learn/offers/slug, not a modal
- Buy Now CTA checks entitlement first; shows "Access in My Library" if owned
- Type-specific preview: video teaser, file name, calendar embed, week overview
- Signed-out users see a sign-in prompt on clicking Buy Now
- Aggregate star rating bar and review cards displayed below the fold
Open questions
- Should a free-tier user see the offer detail and price, or is pricing gated?
- Is there a preview/teaser that anonymous users can access before purchasing?
- Should the offer detail page show creator archetype alignment as a trust signal?
Phase 30 (library) · New
My Library
Image-heavy grid of owned offers with per-type access buttons. Includes the post-checkout "Finalizing..." polling state and empty library state.
Spec highlights
- Library reads from STDB entitlements table filtered by user_identity
- "Finalizing..." card subscribes to entitlements table, auto-dismisses when row appears
- Empty state: "You have not purchased anything yet" with browse CTA
- Returning buyer sees same fulfillment screen without re-purchasing
- Refunded offers: decision needed (badge or hide entirely)
Open questions
- Should the Library show refunded offers with a badge or hide them entirely?
- Is there a download count limit for digital products?
- Should experiment kit enrollment happen automatically on purchase or require a click?
Phase 30 (fulfillment) · New
Per-Type Fulfillment Screens
Four distinct fulfillment views behind the entitlement gate: video player, download button, Cal.com booking embed, and experiment kit enrollment.
Spec highlights
- Each offer type renders a different fulfillment component determined by offer.type
- Download links minted by /api/get-download-link (short TTL, signed, single-use)
- Experiment kit link passes program_id as query param to pre-select the experiment
- Masterclass: signed Cloudflare Stream video player, full-screen on mobile
- Coaching: Cal.com booking embed plus upcoming appointments list
Open questions
- Should video fulfillment support progress tracking (resume playback position)?
- Can buyers share access to a coaching session with a partner or colleague?
- What happens to coaching session fulfillment if the creator cancels their Connect account?
Phase 34 · New
Ratings and Reviews
Buyers rate and review purchases; aggregate star bar and review cards appear on the offer detail page. Write form visible only to entitlement holders.
Spec highlights
- Review write form appears inline on the offer detail page, not in a modal
- Entitlement check is performed server-side by the submit_review reducer
- Each buyer can submit one review per offer; re-submit replaces the first
- Reviews stored in STDB reviews table: slug, identity, rating, body, created_at
- Aggregate rating computed client-side from all reviews rows matching the slug subscription
Open questions
- Should reviews be moderated (admin approval) or instant publish?
- Can creators respond to reviews with a public reply below the review card?
- Should a "Leave a review" prompt appear in My Library right after purchase?
Phase 26 · Complete
Stripe Connect Onboarding
Creator completes Connect Express before selling. Shows not-started, 3-step progress, pending, and enabled states inside Creator Studio sidebar.
Spec highlights
- Server mints Stripe Connect account link (/api/connect-onboard) and redirects to Stripe Express
- On return, CF Function reads charges_enabled and writes to STDB
- Publish button is disabled (not hidden) when charges_enabled is false
- Re-entry into pending state re-fetches account status; no client polling
- Mobile: stepper becomes stacked vertical flow with step counter
Open questions
- Can creators draft offers while Connect is pending, or is the whole composer locked?
- Does the creator get an email from JoyOS or directly from Stripe when their account is enabled?
- What does the Studio surface show if a Connect account is later restricted or deactivated?
Phase 29 · Extended
Offer Composer - All Four Types
Per-type composer variants (masterclass, digital product, coaching, experiment kit) plus draft and published state transitions and the Connect-gate interstitial.
Spec highlights
- Offer type selected at start of composition flow; cannot be changed after saving
- Publish button checks charges_enabled on client; if false, opens Connect gate modal
- Draft offers stored in STDB offers table with status: 'draft'
- File uploads go to R2 (or MinIO fallback) via /api/upload-offer-asset presigned URL
- Conversational AI flow generates the draft; creator refines in structured form
Open questions
- Can a creator have multiple offers of the same type, or one per type?
- Should the AI-guided flow be skippable so creators can go straight to the structured form?
- How does the creator set price: fixed input or Stripe Products price selector?
Phase 33 · Extended
Creator Studio Dashboard
Established creator sees earnings summary and offer performance. Free user hits a welcome screen with "Start Selling" CTA leading to Connect onboarding.
Spec highlights
- Router guard checks user.connectStatus and renders welcome or dashboard accordingly
- Sidebar is the 240px nav rail from studio-b.html locked direction
- Earnings summary cached in KV per creator for 5 minutes (Stripe Connect balance + transfers)
- Offer performance table reads from STDB offer_stats table
- Mobile: sidebar collapses to bottom tab bar; cards become horizontal scroll row
Open questions
- Can a creator use Studio features (like scheduling) before Connect is enabled?
- How granular is the earnings breakdown: total only, or per-offer and per-month with a chart?
- Can a creator deactivate their seller account and return to a pure learner experience?
Phase 28 · New
Refunds and Payouts
Buyer initiates a refund which revokes entitlement. Creator reaches payout dashboard via a server-minted Stripe Express deep link.
Spec highlights
- Refund initiated via /api/request-refund which calls Stripe Refunds API and reverses destination charge
- On successful Stripe refund, CF Function calls revoke_entitlement reducer
- Creator payout summary data comes from Stripe Connect balance endpoint, cached in KV 5 min
- Admin order management panel at /app/admin/orders behind isAdmin gate
- Refunds only allowed within the configured window (default 30 days)
Open questions
- Should partial refunds be supported, or is it always a full refund?
- Does the creator receive a notification when a buyer requests a refund?
- Is there a dispute or appeal flow for creators who disagree with a refund request?
Phase 35 · New
Physical Products
Creator composes a physical offer. Buyer checks out with a shipping address step. Creator marks shipped with a tracking number. Buyer tracks order status in My Library.
Spec highlights
- Physical offer type adds requires_shipping: true flag in STDB offers table
- Checkout detects requires_shipping and inserts the address step before Stripe Payment Element
- Shipping address stored in STDB orders table alongside Stripe Checkout session ID
- Creator order management at /studio/orders; visible only to creators with active offers
- Tracking number update triggers a notifications row for the buyer
Open questions
- Should JoyOS integrate with a shipping label service (EasyPost, Shippo) or leave it to creators?
- Is there a maximum quantity per order, or can buyers purchase multiple units?
- How does inventory management work: auto-unavailable at zero, or manual creator control?
Phase 23 · Complete
Free-Tier + Access Boundary
Public free signup without leaking paid /app/* content. Gate modal shows which tier unlocks content and links to /app/settings for upgrade.
Spec highlights
- Free accounts created at Joy Seeker tier; no credit card required
- Gate modal must show WHICH tier unlocks the content, not just "upgrade"
- TierGate component wraps every paid-only route
- Stripe checkout NOT triggered by free signup; only by explicit Upgrade action
- Gate state persists via STDB users.tier row, not localStorage
Open questions
- Should a free Joy Seeker see a blurred preview of paid content or a hard gate?
- Does free signup require email verification before the account is active?
- Should the gate modal appear as an overlay or navigate to a full upgrade page?
Phase 27 · Complete
Checkout + Entitlements
Buyer purchases an offer through a 3-step flow ending at Stripe-hosted checkout. Post-purchase polling grants entitlement and unlocks the content in My Library.
Spec highlights
- Checkout session created server-side with destination_charge and application_fee_amount
- Success redirect URL is /app/library?new=offer_id to highlight the new purchase
- Entitlement polling checks STDB entitlements table via WebSocket subscription
- Polling times out at 30 seconds with a "refresh" fallback
- Stripe Checkout opens in the same tab so the success redirect lands correctly
Open questions
- How long should the "Finalizing..." polling state wait before showing the timeout fallback?
- Should buyers receive a JoyOS receipt email in addition to Stripe's built-in receipt?
- When a buyer already owns an offer, should "Buy Now" change to "Access" or be hidden?
Phase 32 · New
Featured Shelves and Recommendations
Admin curates horizontal offer shelves with drag-to-reorder. Members see archetype-personalized recommendation rows on the Learning Lab home.
Spec highlights
- Shelves stored in STDB shelves table with sort_order; drag-reorder writes new order
- Platform fee stored in KV as config:platform_fee_pct (encrypted); default 15%
- Recommendation logic reads joy_assessments.conversation for archetypeScores
- Admin shelf manager at /app/admin/shelves behind isAdmin gate
- Horizontal scroll rows use scroll-snap-type: x mandatory for mobile swipe
Open questions
- How many shelves can the admin create? Is there a maximum?
- Should archetype recs pull from any shelf or only from a designated "Recs" shelf?
- Can creators submit their offer to be featured, or is curation purely admin-driven?
Note · Phase 25 Skipped
Marketplace Schema Migration
Pure backend. Publishes all new marketplace tables atomically via CI. No user-facing surface. No mockup needed or appropriate.