Sprint Progress 0 / 5 complete

Revenue Pipeline Flow

📦
Package Gen
Onboarding fields
💳
Payment
Stripe processing
🔑
Key Issued
Auto-generated
⌨️
Key Entry
Client enters key
⬇️
Download
Deliverable asset
DEF-02 · BUG-01 LINKED
Persistent Email Prompt During Bundle Viewing
Bug Critical Path · Resolve First

The "Please enter via email" prompt reappears during bundle viewing even after the field was previously filled and data confirmed saved. The buyer email captured on the onboarding screen is not being passed forward to the Generate Package step — the downstream component reads from a local or uninitialized state rather than from the persisted session/context, meaning the save writes to one location and the prompt reads from another.

Root Cause
Confirmed state handoff failure. The email is being written to one data source during onboarding but the bundle view component is reading from a different, uninitialized source — likely a missing or misplaced React Context Provider boundary across route transitions, or a Firestore listener initializing lazily after the prompt check already fires.
Reproduction
1. Enter buyer email on onboarding screen → 2. Save confirmed → 3. Navigate to bundle/Generate Package view → 4. "Please enter via email" prompt reappears despite prior save.
DEF-01
Selection Highlight Instability in Package Builder
Bug Pre-Revenue Blocker

Selected items in the package builder render with partial highlights, intermittent flashes, and positional "jumping" rather than snapping to a stable, solid selected state. Creates visible unreliability that undermines user trust before reaching checkout.

Root Cause Hypothesis
Selection state is likely being set and then immediately re-evaluated on the same render cycle — causing the highlight to paint, unpaint, then repaint before settling. Classic symptom of: (1) a redundant setState call inside a render-triggered useEffect with the selection array as a dependency, (2) a race condition between local UI state and a syncing async state where a Firestore listener or Redux store update arrives late and overwrites the local optimistic update, or (3) CSS transition conflicts on the .selected class being toggled faster than the transition duration allows.
Reproduction
Open the package builder → select any item → observe highlight behavior on initial click. Look for partial paint, flash, or positional jump before the selected state settles.
BUG-01
Package Generator Re-Asks for Buyer Email
Bug Critical Path

The "Generate Package" step prompts for buyer email even though it was captured on the previous onboarding screen. Data handoff between onboarding and generation is broken — state is not persisting across steps.

Root Cause
Missing data handoff between the onboarding screen and the "generate package" action. The buyer email and company name filled earlier are not being passed forward to the generation step.
Reproduction
1. Enter buyer email + company name on onboarding screen → 2. Proceed to "Generate Package" → 3. System re-prompts for buyer email. First-run toggle exhibited intermittent behavior but the reproducible path expects re-entry. The UI repeatedly asks for an email using the prompt: "Please enter via email." This prompt appears even after fixes were applied and after data is saving. Flickering unsettled bundle selections. It commonly surfaces when viewing bundles.
AT-01
Define Package Scope & Value Proposition
Todo

Design the content, structure, and deliverables for a larger custom package. The small sample used during testing isn't sufficient for client validation — a proper package needs clear, tangible value.

Session Note
"You want to make sure there's value proposition for what they're getting."
Scope Questions to Resolve
What files, agents, and configs does the client receive? What customization is bespoke vs. templated? How is value communicated at the point of purchase?
AT-02
Split Admin vs. Client Agent Trainer Environments
Todo

The Admin Dashboard Agent Trainer must be strictly separated from the client-facing bespoke trainer. Access controls need to prevent clients from seeing or interacting with the admin trainer at any point.

Session Note
"That's not something they should be seeing or having access to."
AT-03
Implement Payment → Key → Download Flow
In Progress Revenue Gate

Build and validate the complete customer-facing transaction: payment processed via Stripe → key auto-issued → client enters key → downloadable asset is delivered. Every step must work before billing is live.

Flow Steps
① Customer pays via Stripe → ② System generates + issues unique key → ③ Customer enters key in portal → ④ System validates key + serves downloadable package asset
AT-04
Fix Package Generation Data Flow
In Progress

Resolve the missing state handoff so onboarding fields (buyer email, company name) persist through to the package generation step without requiring re-entry. This is linked to BUG-01 and DEF-02.

Technical Focus
Identify where context drops between the onboarding form and the generation handler. Likely a missing state pass-through or context provider scope issue. Fix ensures the generator always has access to already-collected buyer data.
AT-05
Prototype Basics, Then Scale to Vending Model
Todo

Validate the core pipeline on a small package first. Once the basic flow is stable and tested, build automated generation of code and files to support a vending-machine model that scales to countless client packages.

Session Note
"Let's start with the basics for now."
Scale Plan
Phase 1: Manually validate small package pipeline end-to-end. Phase 2: Automate code/file generation per client config. Phase 3: Vending-machine model — spin up full package assets on demand.
------MultipartBoundary--YM1QqC9405EbcETnG89nvMoyBoGHEd2qNhaeLVp4Hg---- Content-Type: text/css Content-Transfer-Encoding: binary Content-Location: https://fonts.googleapis.com/css2?family=Geist:wght@300..700&family=Geist+Mono:wght@400;500&display=swap @charset "utf-8"; @font-face { font-family: Geist; font-style: normal; font-weight: 300 700; font-display: swap; src: url("https://fonts.gstatic.com/s/geist/v4/gyByhwUxId8gMEwYGFWfOw.woff2") format("woff2"); unicode-range: U+301, U+400-45F, U+490-491, U+4B0-4B1, U+2116; } @font-face { font-family: Geist; font-style: normal; font-weight: 300 700; font-display: swap; src: url("https://fonts.gstatic.com/s/geist/v4/gyByhwUxId8gMEwSGFWfOw.woff2") format("woff2"); unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } @font-face { font-family: Geist; font-style: normal; font-weight: 300 700; font-display: swap; src: url("https://fonts.gstatic.com/s/geist/v4/gyByhwUxId8gMEwcGFU.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } @font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("https://fonts.gstatic.com/s/geistmono/v4/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2") format("woff2"); unicode-range: U+301, U+400-45F, U+490-491, U+4B0-4B1, U+2116; } @font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("https://fonts.gstatic.com/s/geistmono/v4/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2") format("woff2"); unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } @font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("https://fonts.gstatic.com/s/geistmono/v4/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } @font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("https://fonts.gstatic.com/s/geistmono/v4/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2") format("woff2"); unicode-range: U+301, U+400-45F, U+490-491, U+4B0-4B1, U+2116; } @font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("https://fonts.gstatic.com/s/geistmono/v4/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2") format("woff2"); unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } @font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("https://fonts.gstatic.com/s/geistmono/v4/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } ------MultipartBoundary--YM1QqC9405EbcETnG89nvMoyBoGHEd2qNhaeLVp4Hg---- Content-Type: text/css Content-Transfer-Encoding: binary Content-Location: cid:css-431d0d72-e38f-4cf6-ae57-a8ac5872d1d1@mhtml.blink @charset "utf-8"; :root { --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem); --text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem); --text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem); --text-lg: clamp(1.125rem, 1rem + 0.75vw, 1.5rem); --text-xl: clamp(1.5rem, 1.2rem + 1.25vw, 2.25rem); --space-1: .25rem; --space-2: .5rem; --space-3: .75rem; --space-4: 1rem; --space-5: 1.25rem; --space-6: 1.5rem; --space-8: 2rem; --space-10: 2.5rem; --space-12: 3rem; --radius-sm: .375rem; --radius-md: .5rem; --radius-lg: .75rem; --radius-xl: 1rem; --radius-full: 9999px; --transition: 180ms cubic-bezier(0.16,1,0.3,1); } [data-theme="dark"], :root { --bg: #0d0d0b; --surface: #131311; --surface-2: #181816; --surface-3: #1e1e1b; --surface-4: #252521; --border: rgba(255,255,255,0.08); --border-strong: rgba(255,255,255,0.14); --text: #e8e6e1; --text-muted: #7a7872; --text-faint: #4a4844; --primary: #2dd4bf; --primary-dim: rgba(45,212,191,0.12); --primary-glow: rgba(45,212,191,0.06); --ember: #fb923c; --ember-dim: rgba(251,146,60,0.12); --c-todo: #4a4844; --c-active: #2dd4bf; --c-blocked: #f87171; --c-done: #4ade80; --shadow-sm: 0 1px 3px rgba(0,0,0,0.4); --shadow-md: 0 4px 16px rgba(0,0,0,0.5); --shadow-lg: 0 12px 40px rgba(0,0,0,0.6); } [data-theme="light"] { --bg: #f5f4f0; --surface: #fafaf8; --surface-2: #ffffff; --surface-3: #f0efeb; --surface-4: #e8e7e2; --border: rgba(0,0,0,0.08); --border-strong: rgba(0,0,0,0.14); --text: #1a1916; --text-muted: #6b6966; --text-faint: #b0aea8; --primary: #0d9488; --primary-dim: rgba(13,148,136,0.1); --primary-glow: rgba(13,148,136,0.05); --ember: #ea580c; --ember-dim: rgba(234,88,12,0.1); --c-todo: #9ca3af; --c-active: #0d9488; --c-blocked: #ef4444; --c-done: #22c55e; --shadow-sm: 0 1px 3px rgba(0,0,0,0.08); --shadow-md: 0 4px 16px rgba(0,0,0,0.1); --shadow-lg: 0 12px 40px rgba(0,0,0,0.15); } @media (prefers-color-scheme: light) { :root:not([data-theme]) { --bg: #f5f4f0; --surface: #fafaf8; --surface-2: #ffffff; --surface-3: #f0efeb; --surface-4: #e8e7e2; --border: rgba(0,0,0,0.08); --border-strong: rgba(0,0,0,0.14); --text: #1a1916; --text-muted: #6b6966; --text-faint: #b0aea8; --primary: #0d9488; --primary-dim: rgba(13,148,136,0.1); --primary-glow: rgba(13,148,136,0.05); --ember: #ea580c; --ember-dim: rgba(234,88,12,0.1); --c-todo: #9ca3af; --c-active: #0d9488; --c-blocked: #ef4444; --c-done: #22c55e; --shadow-sm: 0 1px 3px rgba(0,0,0,0.08); --shadow-md: 0 4px 16px rgba(0,0,0,0.1); --shadow-lg: 0 12px 40px rgba(0,0,0,0.15); } } *, ::before, ::after { box-sizing: border-box; margin: 0px; padding: 0px; } html { scroll-behavior: smooth; scroll-padding-top: 4rem; } body { font-family: Geist, Inter, sans-serif; font-size: var(--text-sm); color: var(--text); background: var(--bg); min-height: 100dvh; line-height: 1.6; -webkit-font-smoothing: antialiased; } img, svg { display: block; max-width: 100%; } button { cursor: pointer; background: none; border: none; font: inherit; color: inherit; } a, button { transition: color var(--transition), background var(--transition), border-color var(--transition), opacity var(--transition); } :focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: var(--radius-sm); } .mono { font-family: "Geist Mono", "Courier New", monospace; } .app { display: flex; flex-direction: column; min-height: 100dvh; } .topbar { position: sticky; top: 0px; z-index: 50; display: flex; align-items: center; justify-content: space-between; padding: var(--space-3) var(--space-6); background: color-mix(in oklch, var(--bg) 85%, transparent); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); } .topbar-left { display: flex; align-items: center; gap: var(--space-4); } .logo { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); font-weight: 600; letter-spacing: -0.01em; color: var(--text); text-decoration: none; } .logo-mark { width: 28px; height: 28px; flex-shrink: 0; } .topbar-divider { width: 1px; height: 20px; background: var(--border-strong); } .sprint-label { font-size: var(--text-xs); color: var(--text-muted); font-family: "Geist Mono", monospace; } .topbar-right { display: flex; align-items: center; gap: var(--space-3); } .theme-btn { width: 32px; height: 32px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); } .theme-btn:hover { color: var(--text); background: var(--surface-4); } .date-chip { font-family: "Geist Mono", monospace; font-size: var(--text-xs); color: var(--text-muted); background: var(--surface-3); border: 1px solid var(--border); padding: var(--space-1) var(--space-3); border-radius: var(--radius-full); } main { flex: 1 1 0%; max-width: 1100px; margin-inline: auto; width: 100%; padding: var(--space-8) var(--space-6); } .page-header { margin-bottom: var(--space-8); } .page-header h1 { font-size: var(--text-xl); font-weight: 700; letter-spacing: -0.02em; color: var(--text); margin-bottom: var(--space-2); } .page-header .sub { font-size: var(--text-sm); color: var(--text-muted); display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; } .revenue-banner { margin-top: var(--space-4); padding: var(--space-3) var(--space-5); background: var(--ember-dim); border: 1px solid rgba(251, 146, 60, 0.25); border-radius: var(--radius-lg); font-size: var(--text-xs); color: var(--ember); display: flex; align-items: center; gap: var(--space-2); font-family: "Geist Mono", monospace; } .revenue-banner .icon { font-size: 1rem; } .progress-section { margin-bottom: var(--space-8); } .progress-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-3); } .progress-header .label { font-size: var(--text-xs); color: var(--text-muted); font-family: "Geist Mono", monospace; text-transform: uppercase; letter-spacing: 0.08em; } .progress-header .count { font-size: var(--text-xs); color: var(--text-muted); font-family: "Geist Mono", monospace; } .progress-track { height: 4px; background: var(--surface-4); border-radius: var(--radius-full); overflow: hidden; } .progress-fill { height: 100%; border-radius: var(--radius-full); background: linear-gradient(90deg, var(--primary), color-mix(in oklch, var(--primary) 60%, var(--ember))); transition: width 1s cubic-bezier(0.16, 1, 0.3, 1); width: 0%; } .pipeline-flow { margin-bottom: var(--space-8); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl); padding: var(--space-5) var(--space-6); } .pipeline-flow h2 { font-size: var(--text-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); margin-bottom: var(--space-4); font-family: "Geist Mono", monospace; } .flow-steps { display: flex; align-items: center; gap: 0px; flex-wrap: wrap; overflow-x: auto; padding-bottom: var(--space-2); } .flow-step { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); flex-shrink: 0; } .flow-node { width: 120px; padding: var(--space-3) var(--space-4); background: var(--surface-3); border: 1px solid var(--border-strong); border-radius: var(--radius-lg); text-align: center; transition: background var(--transition), border-color var(--transition); } .flow-node.active { background: var(--primary-dim); border-color: rgba(45, 212, 191, 0.3); } .flow-node .icon { font-size: 1.1rem; margin-bottom: var(--space-1); } .flow-node .node-label { font-size: var(--text-xs); font-weight: 500; color: var(--text); } .flow-node .node-sub { font-size: 10px; color: var(--text-muted); margin-top: 2px; } .flow-arrow { color: var(--text-faint); font-size: 0.875rem; padding: 0 var(--space-2); flex-shrink: 0; align-self: center; margin-top: -18px; } .flow-step.blocked .flow-node { background: rgba(248, 113, 113, 0.08); border-color: rgba(248, 113, 113, 0.25); } .flow-step.blocked .node-label { color: var(--c-blocked); } .sections { display: grid; gap: var(--space-4); } .section-group { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 480px), 1fr)); gap: var(--space-4); } .action-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl); overflow: hidden; transition: border-color var(--transition), box-shadow var(--transition); box-shadow: var(--shadow-sm); } .action-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-md); } .card-header { display: flex; align-items: flex-start; justify-content: space-between; padding: var(--space-5) var(--space-5) var(--space-4); gap: var(--space-3); } .card-title-group { flex: 1 1 0%; min-width: 0px; } .card-num { font-family: "Geist Mono", monospace; font-size: var(--text-xs); color: var(--text-faint); margin-bottom: var(--space-1); } .card-title { font-size: var(--text-base); font-weight: 600; letter-spacing: -0.01em; color: var(--text); margin-bottom: var(--space-2); line-height: 1.3; } .card-badges { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; } .badge { font-family: "Geist Mono", monospace; font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px var(--space-2); border-radius: var(--radius-full); border: 1px solid currentcolor; white-space: nowrap; } .badge-todo { color: var(--c-todo); border-color: var(--c-todo); background: rgba(74, 72, 68, 0.15); } .badge-active { color: var(--c-active); border-color: rgba(45, 212, 191, 0.4); background: var(--primary-dim); } .badge-blocked { color: var(--c-blocked); border-color: rgba(248, 113, 113, 0.4); background: rgba(248, 113, 113, 0.1); } .badge-done { color: var(--c-done); border-color: rgba(74, 222, 128, 0.4); background: rgba(74, 222, 128, 0.1); } .badge-bug { color: var(--ember); border-color: rgba(251, 146, 60, 0.4); background: var(--ember-dim); } .badge-crit { color: rgb(251, 146, 60); border-color: rgba(251, 146, 60, 0.4); background: var(--ember-dim); } .status-btn { width: 28px; height: 28px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; background: var(--surface-3); border: 1px solid var(--border); color: var(--text-muted); font-size: 0.75rem; flex-shrink: 0; } .status-btn:hover { background: var(--surface-4); color: var(--text); } .card-body { padding: 0 var(--space-5) var(--space-5); } .card-desc { font-size: var(--text-sm); color: var(--text-muted); line-height: 1.65; margin-bottom: var(--space-4); } .context-block { background: var(--surface-3); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-3); } .context-block:last-child { margin-bottom: 0px; } .context-block .ctx-label { font-family: "Geist Mono", monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); margin-bottom: var(--space-2); } .context-block .ctx-text { font-size: var(--text-xs); color: var(--text-muted); line-height: 1.6; } .context-block.quote { border-left: 2px solid var(--primary); background: var(--primary-glow); } .context-block.quote .ctx-text { font-style: italic; color: var(--text); } .context-block.warn { border-left: 2px solid var(--ember); background: var(--ember-dim); } .context-block.warn .ctx-text { color: var(--ember); } .checklist { display: flex; flex-direction: column; gap: var(--space-2); } .check-item { display: flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-xs); color: var(--text-muted); cursor: pointer; user-select: none; } .check-item:hover { color: var(--text); } .check-box { width: 16px; height: 16px; border-radius: var(--radius-sm); border: 1px solid var(--border-strong); flex-shrink: 0; margin-top: 2px; display: flex; align-items: center; justify-content: center; transition: background var(--transition), border-color var(--transition); } .check-item.checked .check-box { background: var(--primary); border-color: var(--primary); } .check-item.checked .check-label { text-decoration: line-through; color: var(--text-faint); } .check-box svg { display: none; } .check-item.checked .check-box svg { display: block; } .action-card.wide { grid-column: 1 / -1; } .action-card.bug-card { border-color: rgba(251, 146, 60, 0.2); } .action-card.bug-card:hover { border-color: rgba(251, 146, 60, 0.4); } .section-label { font-family: "Geist Mono", monospace; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-faint); margin-bottom: var(--space-3); display: flex; align-items: center; gap: var(--space-3); } .section-label::after { content: ""; flex: 1 1 0%; height: 1px; background: var(--border); } ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--surface-4); border-radius: var(--radius-full); } @media (max-width: 640px) { .topbar { padding: var(--space-3) var(--space-4); } main { padding: var(--space-6) var(--space-4); } .flow-steps { gap: 0px; } .flow-node { width: 96px; padding: var(--space-2) var(--space-3); } .date-chip { display: none; } } @keyframes fadeUp { 0% { opacity: 0; transform: translateY(10px); } 100% { opacity: 1; transform: translateY(0px); } } .action-card { animation: 0.35s ease 0s 1 normal both running fadeUp; } .action-card:nth-child(1) { animation-delay: 0.05s; } .action-card:nth-child(2) { animation-delay: 0.1s; } .action-card:nth-child(3) { animation-delay: 0.15s; } .action-card:nth-child(4) { animation-delay: 0.2s; } .action-card:nth-child(5) { animation-delay: 0.25s; } @media (prefers-reduced-motion: reduce) { .action-card { animation: auto ease 0s 1 normal none running none; } .progress-fill { transition: none; } }