ADR-002: Separate BaselineWelcome from BaselineAssessmentSDK
Status
Accepted
Date: 2025-11-25 (Approximate)
Context
The baseline assessment flow has two distinct screens:
- Welcome Screen: Introduces the user to baseline assessment, explains what it measures, shows benefits
- "What to Expect" Screen: Shows 5 specific bullet points about the assessment process, has the "Start" button
- Conversation Screen: The actual ElevenLabs conversation with Jodie
Originally, all three screens were managed within a single BaselineAssessmentSDK.tsx component using internal state to toggle between them. This created a large component (~800 lines) with multiple responsibilities.
Problems with the monolithic approach:
- Single component had too many responsibilities (welcome UI + conversation logic)
- Difficult to reuse welcome screen without pulling in SDK dependencies
- Testing was complex - had to mock entire ElevenLabs SDK to test welcome UI
- Welcome screen layout changes required touching conversation logic
- Component file was becoming difficult to navigate
User requirements:
- Two-screen flow before starting conversation: Welcome → What to Expect → Conversation
- Consistent branding and smooth transitions
- Ability to go back from "What to Expect" to "Welcome"
Decision
We will split the baseline assessment into two separate components:
-
BaselineWelcome.tsx:- Exports
BaselineAssessmentScreencomponent - Manages internal state for Welcome → "What to Expect" transition
- No ElevenLabs SDK dependencies
- Takes
onStartAssessmentcallback prop
- Exports
-
BaselineAssessmentSDK.tsx:- Handles only the conversation screen
- Uses ElevenLabs SDK
- Manages conversation state and assessment data
- Takes
onCompleteandonBackcallback props
Component responsibilities:
// BaselineWelcome.tsx
export function BaselineAssessmentScreen({
onStartAssessment
}: {
onStartAssessment: () => void
}) {
const [showWelcome, setShowWelcome] = useState(true);
// Internal: Welcome → What to Expect
// External: What to Expect → calls onStartAssessment()
}
// BaselineAssessmentSDK.tsx
export function BaselineAssessmentSDK({
onComplete,
onBack
}: {
onComplete: () => void;
onBack?: () => void;
}) {
// Only handles conversation and assessment logic
}Flow in MobileAppStructure.tsx:
case 'baseline_welcome':
return <BaselineAssessmentScreen
onStartAssessment={() => setOnboardingScreen('baseline_assessment')}
/>;
case 'baseline_assessment':
return <BaselineAssessmentSDK
onComplete={() => { /* go to dashboard */ }}
onBack={() => setOnboardingScreen('baseline_welcome')}
/>;Consequences
Positive Consequences
- Separation of concerns: UI component separate from SDK logic
- Better testability: Can test welcome screens without mocking SDK
- Easier maintenance: Changes to welcome UI don't touch conversation logic
- Smaller files: Each component is focused and easier to understand
- Reusability: Could reuse welcome screen for other contexts
- Clearer state management: Each component manages its own internal state
Negative Consequences
- More files: Two components instead of one
- Prop drilling: Need to pass callbacks through MobileAppStructure
- Potential confusion: Developers need to know about both components
- Duplication risk: Some styling might be duplicated between components
Neutral Consequences
- Same user experience, different implementation
- No performance impact
- Total lines of code stays roughly the same
Alternatives Considered
Alternative 1: Keep everything in one component
- Pros: Single source of truth, no prop passing
- Cons: Large file, mixed responsibilities, hard to test
- Why not chosen: Component was becoming too large and difficult to maintain
Alternative 2: Three separate components (Welcome, WhatToExpect, Conversation)
- Pros: Maximum separation, each screen is independent
- Cons: More files, more complex state management, Welcome and WhatToExpect are closely related
- Why not chosen: Overengineering for the current needs
Alternative 3: Use a state machine library (XState)
- Pros: Explicit state transitions, visual state diagrams
- Cons: Additional dependency, learning curve, overkill for linear flow
- Why not chosen: Simple useState is sufficient for this flow
Files Affected
New Files:
src/components/mobile/BaselineWelcome.tsx- Welcome screen component
Modified Files:
src/components/mobile/BaselineAssessmentSDK.tsx- Removed welcome screen logicsrc/components/mobile/MobileAppStructure.tsx- Updated to use both components
Imports:
- Components importing baseline assessment need to know about the two-step flow
Rollback Safety
This is a relatively safe refactoring to revert.
If this decision needs to be reverted:
-
Get the original combined component:
git log --all --full-history -- "**/BaselineAssessmentSDK.tsx" | head -20 # Find commit before the split git show <commit>:src/components/mobile/BaselineAssessmentSDK.tsx > BaselineAssessmentSDK.backup.tsx -
Restore the monolithic component:
- Copy welcome screen JSX from
BaselineWelcome.tsxback intoBaselineAssessmentSDK.tsx - Add back internal state for screen toggling
- Remove
onStartAssessmentprop, use internal state instead
- Copy welcome screen JSX from
-
Update MobileAppStructure:
case 'baseline_welcome': // Change to render BaselineAssessmentSDK directly return <BaselineAssessmentSDK onComplete={...} />; -
Delete the separate file:
git rm src/components/mobile/BaselineWelcome.tsx
Critical checks before reverting:
- Verify both welcome screens are present in combined component
- Test Welcome → What to Expect → Conversation flow
- Check that back button works
- Verify all text is correct ("Five questions", not "Six")
- Update
scripts/check-layout.jsif needed
Breaking changes introduced:
BaselineWelcomecomponent created (new export)BaselineAssessmentSDKno longer shows welcome screensMobileAppStructureneeds to handle two-step flow
Implementation Notes
Key patterns to follow:
-
BaselineWelcome manages internal transitions:
const [showWelcome, setShowWelcome] = useState(true); if (showWelcome) { return <WelcomeUI onContinue={() => setShowWelcome(false)} />; } return <WhatToExpectUI onStart={onStartAssessment} />; -
MobileAppStructure handles cross-component navigation:
const [onboardingScreen, setOnboardingScreen] = useState('baseline_welcome'); // baseline_welcome → baseline_assessment // baseline_assessment → null (dashboard) -
Each component is self-contained:
- BaselineWelcome: Own logo, title, cards
- BaselineAssessmentSDK: Own header, conversation UI
Common pitfalls to avoid:
- ❌ Don't forget to import both components in MobileAppStructure
- ❌ Don't remove the "What to Expect" screen from BaselineAssessmentSDK if it's being used there (check current version)
- ❌ Don't mix the two approaches (internal state + external state)
Testing requirements:
- Test Welcome screen renders correctly
- Test "What to Expect" screen appears after Welcome
- Test onStartAssessment callback fires
- Test conversation starts after "What to Expect"
- Test back button returns to welcome flow
Documentation to update:
- Component architecture diagram (if one exists)
- Developer onboarding guide
- Testing documentation
References
- React Component Composition Patterns (opens in a new tab)
- Related: ADR-001: SDK Migration - The SDK split made this refactoring cleaner
- GitHub issue discussing component size (opens in a new tab) (TODO: Add if exists)
Review History
- 2025-11-25: Implemented during UI refinement sprint
- 2025-12-08: Documented after rollback revealed interaction with SDK migration
- 2025-12-08: Verified both components exist and work correctly