Skip to content

Phase 1: MVP - Trip Planner Mini-App

Version: 1.2 Date: 2025-11-10 Updated: 2026-01-07 Duration: 6-8 weeks Status: In Progress - Handler Layer Complete Goal: Validate multiplayer mini-app architecture with one production use case

⚠️ Terminology Note (January 2026)

This spec references the "superpowers" folder which has been renamed to "workflows": - app/superpowers/ β†’ app/workflows/

New terminology: - Workflows: Automated background tasks (Travel Brain, Calendar, etc.) in app/workflows/ - Superpowers: User-created custom apps via conversation in app/miniapps/generator/


Current Progress (December 2025)

Completed

  • βœ… Handler-based routing layer (all 4 mini-apps)
  • βœ… Session management and persistence
  • βœ… Web UI configuration generation
  • βœ… View-specific layouts (overview, map, venue, settlement, results)
  • βœ… URL generation for web access
  • βœ… Trip Planner with multi-venue parsing and attribution
  • βœ… Bill Split with settlement calculations
  • βœ… Todo List with priorities and assignments
  • βœ… Polls with voting and results

In Progress

  • πŸ”„ Frontend web renderer (archety-web)
  • πŸ”„ Integration testing

Pending

  • ⏳ Event-sourcing integration for full audit trail
  • ⏳ Real-time sync for multiplayer

Objectives

  1. Build Foundation: Event sourcing, room management, UI blocks
  2. Ship MVP: Trip Planner mini-app working in iMessage
  3. Validate Architecture: Prove multiplayer patterns work at small scale
  4. Gather Learnings: User feedback to inform Phase 2

Success Metrics: - βœ… 20+ groups use Trip Planner for real trips - βœ… 80% task completion rate (users complete trip planning) - βœ… <100ms p95 latency for state updates - βœ… Zero data loss or corruption incidents - βœ… β‰₯4.5/5 average β€œfeels like a friend” onboarding survey - βœ… β‰₯25% of beta users add at least one additional mini-app via drawer


Scope

In Scope

Core Infrastructure: - βœ… Event Store (DynamoDB) - βœ… Room Manager - βœ… State Coordinator - βœ… 5 UI Blocks (Poll, Card, Table, Text, Quick Reply) - βœ… Real-Time Sync (polling-based for iMessage) - βœ… Tone Orchestrator + Persona quiz onboarding - βœ… Consent Registry & proactive scheduler (baseline rules) - βœ… Curated App Drawer & highlight cards (3-5 templates) - βœ… Minimal Remix lineage tracking (link remixed template back to parent)

Trip Planner Features: - βœ… Create shared trip room - βœ… Add participants with playful intros - βœ… Collaborative activity planning - βœ… Voting on activities (poll block) - βœ… Basic expense tracking - βœ… Itinerary summary with screenshot-worthy formatting - βœ… Consent-aware calendar/email peek with in-line acknowledgements

Platforms: - βœ… iMessage (primary, messaging-first UX) - ⏸️ Telegram (nice-to-have if time permits) - ⏸️ Web App Drawer (read-only prototype for QA)

Out of Scope (Phase 2+)

  • ❌ Flight/hotel booking integration
  • ❌ Real-time WebSocket sync (use polling MVP)
  • ❌ User-generated mini-app creation UI (beyond curated templates)
  • ❌ Advanced expense splitting
  • ❌ Photo sharing in trips
  • ❌ Full trending algorithm + friend feed (basic featured list only in Phase 1)

Architecture Deliverables

Week 1-2: Foundation Layer

1. Event Store Service

File: app/miniapps/event_store.py

Components:

class EventStore:
    async def append_event(app_id, room_id, event) -> str
    async def get_events(app_id, room_id, since=None) -> List[Event]
    async def get_event_count(app_id, room_id) -> int

DynamoDB Setup:

# Create table via AWS CLI or Terraform
aws dynamodb create-table \
    --table-name miniapp-events \
    --attribute-definitions \
        AttributeName=partition_key,AttributeType=S \
        AttributeName=sort_key,AttributeType=S \
    --key-schema \
        AttributeName=partition_key,KeyType=HASH \
        AttributeName=sort_key,KeyType=RANGE \
    --billing-mode PAY_PER_REQUEST

Tests: - test_event_store.py - βœ… Append event successfully - βœ… Get events in chronological order - βœ… Handle concurrent appends - βœ… TTL deletes old events

Estimated Effort: 3 days


2. Room Manager

File: app/miniapps/room_manager.py

Components:

class RoomManager:
    async def create_room(app_id, creator_id, participants) -> str
    async def join_room(room_id, user_id)
    async def leave_room(room_id, user_id)
    async def get_room(room_id) -> Room
    async def get_user_rooms(user_id, app_id=None) -> List[Room]
    async def is_participant(room_id, user_id) -> bool

Database Migration:

-- Migration: 001_create_rooms.sql
CREATE TABLE miniapp_rooms (
    room_id VARCHAR(255) PRIMARY KEY,
    app_id VARCHAR(255) NOT NULL,
    creator_id VARCHAR(255) NOT NULL,
    participants VARCHAR(255)[] NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    last_active TIMESTAMP DEFAULT NOW(),
    metadata JSONB,
    status VARCHAR(50) DEFAULT 'active'
);

CREATE INDEX idx_rooms_app ON miniapp_rooms(app_id);
CREATE INDEX idx_rooms_participants ON miniapp_rooms USING GIN(participants);

Tests: - test_room_manager.py - βœ… Create room with participants - βœ… Join/leave room - βœ… List user's rooms - βœ… Validate participant access

Estimated Effort: 3 days


3. State Coordinator

File: app/miniapps/state_coordinator.py

Components:

class StateCoordinator:
    async def get_current_state(app_id, room_id) -> Dict
    async def apply_event(state, event) -> Dict
    async def resolve_conflict(event1, event2) -> Event
    async def invalidate_cache(app_id, room_id)

Event Replay Logic:

async def get_current_state(app_id: str, room_id: str) -> Dict:
    # 1. Check cache
    cached = await redis.get(f"room_state:{app_id}:{room_id}")
    if cached:
        return json.loads(cached)

    # 2. Load events
    events = await event_store.get_events(app_id, room_id)

    # 3. Replay to reconstruct state
    state = get_initial_state(app_id)  # App-specific
    for event in events:
        state = apply_event(state, event)

    # 4. Cache for 5 minutes
    await redis.setex(f"room_state:{app_id}:{room_id}", 300, json.dumps(state))

    return state

Tests: - test_state_coordinator.py - βœ… Reconstruct state from events - βœ… Apply events correctly - βœ… Cache invalidation - βœ… Conflict resolution (LWW)

Estimated Effort: 4 days


4. UI Block System (5 Blocks)

Files:

app/miniapps/blocks/
β”œβ”€β”€ base.py          # Block interface
β”œβ”€β”€ text.py          # Text block
β”œβ”€β”€ quick_reply.py   # Quick reply buttons
β”œβ”€β”€ poll.py          # Voting poll
β”œβ”€β”€ card.py          # Rich card
β”œβ”€β”€ table.py         # Table/list
└── renderers/
    β”œβ”€β”€ imessage.py  # iMessage formatter
    └── telegram.py  # Telegram formatter (stub)

Base Interface:

class Block(ABC):
    @abstractmethod
    def to_json(self) -> Dict:
        """Serialize block to JSON schema"""
        pass

    @abstractmethod
    def render(self, platform: str) -> str:
        """Render block for platform"""
        pass

    @abstractmethod
    async def handle_interaction(self, user_id: str, value: Any) -> Event:
        """Handle user interaction"""
        pass

Example: Poll Block

class PollBlock(Block):
    def __init__(self, question: str, options: List[str], room_id: str):
        self.question = question
        self.options = options
        self.room_id = room_id
        self.votes = {}  # user_id -> option_index

    def render(self, platform: str) -> str:
        if platform == "imessage":
            text = f"πŸ—³οΈ {self.question}\n\n"
            for i, option in enumerate(self.options, 1):
                vote_count = list(self.votes.values()).count(i-1)
                text += f"{i}. {option}  [{'πŸ‘€' * vote_count}] ({vote_count})\n"
            text += f"\nReply 1-{len(self.options)} to vote"
            return text
        # ... telegram renderer

    async def handle_interaction(self, user_id: str, value: Any) -> Event:
        # User voted
        option_index = int(value) - 1
        self.votes[user_id] = option_index

        return MiniAppEvent(
            action="poll_voted",
            payload={"option": option_index, "voter": user_id},
            room_id=self.room_id
        )

Tests: - test_blocks.py - βœ… Each block renders correctly - βœ… Interactions create events - βœ… State updates from events

Estimated Effort: 5 days


Week 3-4: Trip Planner Application

5. Trip Planner Workflow

File: app/superpowers/catalog/multiplayer/trip_planner.py

Data Model:

@dataclass
class TripPlannerState:
    trip_id: str
    name: str
    destination: Dict[str, str]  # {"city": "Tokyo", "country": "Japan"}
    dates: Dict[str, str]  # {"start": "2026-04-03", "end": "2026-04-10"}
    participants: Dict[str, ParticipantInfo]
    activities: List[Activity]
    hotels: List[Hotel]
    expenses: List[Expense]

@dataclass
class Activity:
    id: str
    name: str
    day: int
    time: Optional[str]
    votes: Dict[str, str]  # user_id -> "yes"/"no"/"maybe"
    status: str  # "proposed" / "confirmed" / "rejected"

Event Types:

# Trip Planner Events
- trip_created
- participant_added
- destination_set
- dates_set
- activity_proposed
- activity_voted
- activity_confirmed
- expense_logged

Workflow Nodes:

trip_planner_workflow = Workflow(
    id="trip_planner",
    name="Trip Planner",
    category="multiplayer",
    trigger=WorkflowTrigger(type="manual", config={"keywords": ["plan a trip", "trip planner"]}),
    nodes=[
        # 1. Initialize trip
        WorkflowNode(
            id="create_trip",
            type="create_room",
            config={
                "app_id": "trip_planner",
                "participants": "{{$context.participants}}"
            }
        ),

        # 2. Ask for destination
        WorkflowNode(
            id="ask_destination",
            type="wait_for_input",
            config={
                "prompt": "Where are you planning to go?",
                "save_to": "destination"
            }
        ),

        # 3. Ask for dates
        WorkflowNode(
            id="ask_dates",
            type="wait_for_input",
            config={
                "prompt": "What are your travel dates? (e.g., April 3-10)",
                "save_to": "dates"
            }
        ),

        # 4. Initialize state
        WorkflowNode(
            id="init_state",
            type="set_room_state",
            config={
                "key": "trip_data",
                "value": {
                    "destination": "{{$node.ask_destination.response}}",
                    "dates": "{{$node.ask_dates.response}}",
                    "activities": [],
                    "expenses": []
                }
            }
        ),

        # 5. Send confirmation
        WorkflowNode(
            id="send_confirmation",
            type="render_block",
            config={
                "block": {
                    "type": "text",
                    "content": "πŸŽ‰ Trip to {{$state.destination}} created! Everyone can now add activities and vote."
                }
            }
        )
    ]
)

State Management: - Initial state created on trip creation - Each vote/add emits event - State rebuilt from events on load

Estimated Effort: 6 days


6. Trip Planner Interaction Handlers

File: app/miniapps/apps/trip_planner/handlers.py

Key Handlers:

class TripPlannerHandlers:
    async def handle_activity_proposal(self, room_id: str, user_id: str, activity_name: str):
        """User proposes new activity"""
        activity_id = generate_id()

        event = MiniAppEvent(
            action="activity_proposed",
            payload={
                "activity_id": activity_id,
                "name": activity_name,
                "proposed_by": user_id
            },
            room_id=room_id,
            app_id="trip_planner",
            user_id=user_id
        )

        await event_store.append_event("trip_planner", room_id, event)

        # Broadcast to all participants
        await self.broadcast_update(room_id, event)

        # Send poll for voting
        poll = PollBlock(
            question=f"Add {activity_name} to the trip?",
            options=["Yes πŸ‘", "No πŸ‘Ž", "Maybe πŸ€”"],
            room_id=room_id
        )

        await self.send_to_room(room_id, poll)

    async def handle_vote(self, room_id: str, user_id: str, activity_id: str, vote: str):
        """User votes on activity"""
        event = MiniAppEvent(
            action="activity_voted",
            payload={
                "activity_id": activity_id,
                "vote": vote,
                "voter": user_id
            },
            room_id=room_id,
            app_id="trip_planner",
            user_id=user_id
        )

        await event_store.append_event("trip_planner", room_id, event)

        # Check if voting complete (all participants voted)
        state = await state_coordinator.get_current_state("trip_planner", room_id)
        activity = next(a for a in state["activities"] if a["id"] == activity_id)

        all_voted = len(activity["votes"]) == len(state["participants"])

        if all_voted:
            # Determine result
            yes_votes = sum(1 for v in activity["votes"].values() if v == "yes")
            majority = yes_votes > len(state["participants"]) / 2

            if majority:
                await self.confirm_activity(room_id, activity_id)
            else:
                await self.reject_activity(room_id, activity_id)

Tests: - test_trip_planner.py - βœ… Create trip flow - βœ… Add activity - βœ… Vote on activity - βœ… Concurrent votes resolve correctly - βœ… Expense logging

Estimated Effort: 5 days


Week 5: Integration & Enhancements

7. Enhance Existing Components

WorkflowEngine Enhancement:

File: app/superpowers/engine.py

Changes:

class WorkflowEngine:
    async def execute_workflow(
        self,
        workflow_id: str,
        user_id: str,
        trigger_data: Dict[str, Any],
        room_id: Optional[str] = None,  # NEW
        room_context: Optional[RoomContext] = None  # NEW
    ) -> ExecutionResult:
        # Load room state if multiplayer
        if room_id:
            room_state = await state_coordinator.get_current_state(workflow_id, room_id)
        else:
            room_state = None

        context = ExecutionContext(
            execution_id=generate_id(),
            workflow_id=workflow_id,
            user_id=user_id,
            trigger_data=trigger_data,
            room_id=room_id,
            room_state=room_state,
            is_multiplayer=bool(room_id)
        )

        # Execute nodes...
        for node in workflow.nodes:
            result = await self.execute_node(node, context)

            # If multiplayer, emit events
            if context.is_multiplayer:
                await self.emit_node_result_event(context, node, result)

        return ExecutionResult(...)

Estimated Effort: 3 days


Intent Classifier Enhancement:

File: app/superpowers/intent_classifier.py

Changes:

async def classify_intent(
    message: str,
    user_id: str,
    context: Optional[ConversationContext] = None
) -> ClassificationResult:
    is_group = context and context.is_group_chat

    prompt = f"""
    User message: {message}
    Context: {"Group chat" if is_group else "1:1 chat"}

    Determine:
    1. Is this starting a Trip Planner session?
    2. Is this interacting with an existing Trip Planner (voting, adding activity)?
    3. Other workflow?

    Keywords for Trip Planner: "plan a trip", "trip planner", "tokyo trip", etc.
    """

    result = await llm.complete(prompt)

    return ClassificationResult(
        workflow_id=result.workflow_id,
        is_new_instance=result.is_new,
        requires_room=is_group and result.workflow_id == "trip_planner"
    )

Estimated Effort: 2 days


8. Tone Orchestrator & Persona Quiz

Files: app/persona/tone_orchestrator.py, app/persona/passport.py

Deliverables: - Tone script loader (tone_scripts table) with fallback templates. - Helper emit_bubbles to split multi-bubble responses with configurable delay. - Onboarding quiz quick replies mapped to Persona Passport updates. - Sentiment hook to prepend empathy copy when stress detected.

QA: Snapshot golden conversations to ensure tone stays casual, emoji cadence correct.

Estimated Effort: 3 days (parallelizable with Week 3 block work)


9. Minimal App Drawer & Catalog

Files: app/miniapps/drawer/service.py, app/miniapps/marketplace.py, edge_agent/drawer_sync.py

Deliverables: - Hardcoded curated templates (Trip Planner, Mood Check-In, Steps Challenge). - Drawer API (GET/POST /drawer), pin/snooze support. - Highlight card block (β€œYour week in three vibes”) triggered Sundays 6pm. - Edge agent sync command to render drawer entries in chat on demand. - Remix lineage stub storing remix_parent_id + remix_count.

Estimated Effort: 4 days


10. Message Polling Sync (Real-Time Lite)

File: app/miniapps/sync_service.py

Polling-Based Sync:

class PollingSync:
    """
    Lightweight sync for iMessage (no WebSockets)
    Edge agent polls every 2 seconds for updates
    """

    async def check_for_updates(self, user_id: str, since: datetime) -> List[Event]:
        # Find all rooms user is in
        rooms = await room_manager.get_user_rooms(user_id)

        updates = []
        for room in rooms:
            # Get events since last poll
            events = await event_store.get_events(
                room.app_id,
                room.room_id,
                since=since.isoformat()
            )

            # Filter out user's own events (they already saw them)
            other_user_events = [e for e in events if e.user_id != user_id]

            updates.extend(other_user_events)

        return updates

Edge Agent Integration:

# In Mac Mini edge agent
async def poll_for_updates():
    while True:
        last_poll = get_last_poll_time()

        updates = await api.post("/miniapps/sync/poll", {
            "user_id": user_phone,
            "since": last_poll
        })

        for update in updates:
            # Send as iMessage
            await send_imessage(user_phone, format_event_update(update))

        save_last_poll_time(datetime.now())
        await asyncio.sleep(2)  # Poll every 2 seconds

Estimated Effort: 3 days


11. Group Orchestrator & Host Management

File: app/miniapps/group_orchestrator.py

Purpose: Implement single-assistant-per-group model with collision prevention and host management.

Deliverables: 1. Group Detection: - Detect group chat vs. 1:1 based on participant count - Route group messages to GroupOrchestrator first - Check for existing host assistant before creating room

  1. Collision Prevention:

    async def handle_assistant_added_to_group(
        assistant_phone: str,
        chat_guid: str
    ):
        existing_host = await get_room_host(chat_guid)
    
        if existing_host and existing_host != assistant_phone:
            # Mute new assistant
            await mark_assistant_muted(assistant_phone, chat_guid)
    
            # DM owner
            owner = await get_assistant_owner(assistant_phone)
            await send_dm(owner,
                f"Heads up! {existing_host.persona_name} is already "
                f"managing that group. I'll stay in your DMs if you need me 😊"
            )
            return  # Don't respond in group
    

  2. Host Transfer:

    async def transfer_host(
        room_id: str,
        current_host: str,
        new_host_user: str
    ):
        new_host_assistant = await get_user_assistant(new_host_user)
    
        await update_room_host(room_id, new_host_user, new_host_assistant)
    
        await send_to_group(room_id,
            f"Host transferred to @{new_host_user}! "
            f"Their assistant is now managing this room."
        )
    

  3. Personal Summary (Cross-Assistant Read):

    async def get_personal_summary(user_id: str, query: str):
        room_id = await extract_room_from_query(query)
    
        if not await room_manager.is_participant(room_id, user_id):
            return "I don't think you're in that group πŸ€”"
    
        # Read-only access to group state
        state = await state_coordinator.get_persona_snapshot(room_id)
    
        # Format in user's persona voice
        user_persona = await get_user_persona(user_id)
        return await persona_styler.format_summary(state, user_persona)
    

  4. Rate Limiting with Digest Mode:

    async def check_message_rate(room_id: str) -> bool:
        recent_count = await count_messages(room_id, window=60)
    
        if recent_count > 20:  # 20 msgs/min threshold
            await enable_digest_mode(room_id)
            await send_to_group(room_id,
                "Whoa, lots happening! I'll batch my updates "
                "for the next few minutes so we don't clog the chat πŸ˜…"
            )
            return False
    
        return True
    

Database Updates:

ALTER TABLE miniapp_rooms
    ADD COLUMN host_user_id VARCHAR(255),
    ADD COLUMN host_assistant_phone VARCHAR(255),
    ADD COLUMN digest_mode BOOLEAN DEFAULT FALSE,
    ADD COLUMN muted_assistants VARCHAR(255)[] DEFAULT '{}';

CREATE INDEX idx_rooms_host_assistant ON miniapp_rooms(host_assistant_phone);

Container Orchestration: - Deploy 1 container per assistant phone number (not per room) - Each assistant manages 5-10 rooms on average - Auto-scale at >10 active rooms per assistant - Cold start <2s, scale-down after 5 min inactivity

Integration Tests:

tests/integration/
β”œβ”€β”€ test_group_single_assistant.py
β”‚   - test_first_assistant_becomes_host()
β”‚   - test_second_assistant_auto_mutes()
β”‚   - test_muted_assistant_dm_sent()
β”œβ”€β”€ test_group_host_transfer.py
β”‚   - test_transfer_preserves_state()
β”‚   - test_new_host_receives_notification()
β”œβ”€β”€ test_group_personal_summary.py
β”‚   - test_cross_assistant_read_only()
β”‚   - test_summary_in_persona_voice()
└── test_group_rate_limiting.py
    - test_digest_mode_activates_at_threshold()
    - test_messages_batched_during_digest()

Estimated Effort: 4 days

Reference: ADR 003: Single Assistant Per Group


Week 6: Testing & Polish

9. End-to-End Testing

Test Scenarios:

  1. Happy Path: Create Trip & Add Activities
  2. User A starts trip planner in group chat
  3. System creates room with all participants
  4. User A sets destination & dates
  5. User B proposes activity
  6. All users vote
  7. Activity confirmed

  8. Concurrent Voting

  9. User A and User B vote simultaneously
  10. Both votes recorded
  11. No data loss
  12. Results updated correctly

  13. Offline β†’ Online Sync

  14. User A goes offline
  15. User B adds 3 activities
  16. User A comes back online
  17. Polls for updates
  18. Receives all 3 activities

  19. Error Handling

  20. Invalid vote (typo in number)
  21. Non-participant tries to join
  22. OAuth expired during workflow

Test Files:

tests/integration/
β”œβ”€β”€ test_trip_planner_e2e.py
β”œβ”€β”€ test_concurrent_voting.py
β”œβ”€β”€ test_offline_sync.py
└── test_error_scenarios.py

Estimated Effort: 5 days


10. UI/UX Polish

Persona Integration: - All Trip Planner outputs styled by Sage/Echo - Conversational prompts instead of robotic text - Emoji usage for visual hierarchy

Example Output (Before):

Trip created. Destination: Tokyo. Dates: April 3-10.
Add activities by replying "add [activity name]"

Example Output (After - Sage Style):

omg Tokyo?? πŸ—Ό yes yes yes! Your trip is April 3-10 and I'm here to help y'all plan every second of it.

Anyone can throw out activity ideas and we'll vote on them. Just tell me what you're thinking!

Estimated Effort: 3 days


Week 7-8: Beta Testing & Iteration

11. Internal Beta

Participants: - 5 groups from team + friends - Mix of 2-5 people per group - Real trips (not test scenarios)

Metrics Tracked: - Task completion rate - Time to complete trip planning - Number of activities added - Voting participation - Subjective satisfaction (survey)

Feedback Loops: - Daily Slack updates - Weekly retro meetings - Bug reports via GitHub

Estimated Duration: 1 week


12. External Beta (Limited)

Participants: - 20 groups (recruited via social media) - Incentive: $50 Amazon gift card for completing trip

Onboarding: - Personal phone call to explain system - Invite to group chat with Sage - Follow-up check-ins

Data Collection: - Amplitude analytics - User interviews (5 groups) - Screenshot analysis (what gets shared)

Estimated Duration: 1 week


Deliverables

Code

  • βœ… Event Store service
  • βœ… Room Manager
  • βœ… State Coordinator
  • βœ… 5 UI Blocks
  • βœ… Trip Planner workflow
  • βœ… Enhanced WorkflowEngine
  • βœ… Enhanced Intent Classifier
  • βœ… Polling sync service
  • βœ… Integration tests
  • βœ… Documentation

Infrastructure

  • βœ… DynamoDB table provisioned
  • βœ… PostgreSQL schema migrated
  • βœ… Redis cache configured
  • βœ… Monitoring dashboards (Amplitude)

Documentation

  • βœ… Architecture diagrams
  • βœ… API documentation
  • βœ… User guide (how to use Trip Planner)
  • βœ… Developer guide (how to build mini-apps)

Success Criteria

Technical

  • βœ… Zero data loss in beta
  • βœ… p95 latency <100ms for state reads
  • βœ… 99.9% event delivery success rate
  • βœ… No critical bugs in production

Product

  • βœ… 80% of beta users complete a trip plan
  • βœ… Average 4+ activities per trip
  • βœ… 90%+ voting participation
  • βœ… 4.0+ satisfaction rating (1-5 scale)

Business

  • βœ… 15%+ users share screenshots on social media
  • βœ… 1.2+ viral coefficient (1 user β†’ 1.2 new users)
  • βœ… Ready to scale to Phase 2 (Fitness Challenge)

Risks & Mitigation

Risk 1: Event Store Performance Issues

Likelihood: Medium Impact: High

Mitigation: - Load test with 1000 concurrent rooms - Set up DynamoDB auto-scaling - Have rollback plan (disable multiplayer, fall back to solo mode)

Risk 2: Confusing UX for Group Interactions

Likelihood: High Impact: Medium

Mitigation: - Extensive user testing before beta - Iterate on copy/prompts - Provide onboarding guidance

Risk 3: OAuth Friction in Group Context

Likelihood: Medium Impact: Medium

Mitigation: - Only require OAuth for organizer (not all participants) - Clear messaging about why OAuth needed - Graceful fallback if OAuth fails


Timeline Summary

Week Milestone Deliverables
1-2 Foundation Event Store, Room Manager, State Coordinator
3 UI Blocks 5 block types implemented
4 Trip Planner Workflow + handlers
5 Integration Enhanced engine, intent classifier, sync
6 Testing E2E tests, UI polish
7 Internal Beta 5 groups test
8 External Beta 20 groups test

Total: 8 weeks


Go/No-Go Decision Points

After Week 2 (Foundation Complete)

Criteria: - Event store handles 1000 writes/sec - Room management works correctly - State reconstruction <50ms

If Not Met: Add 1 week buffer

After Week 6 (Pre-Beta)

Criteria: - All E2E tests pass - No critical bugs - UX feels polished

If Not Met: Delay beta, fix issues

After Week 8 (Beta Complete)

Criteria: - 80%+ task completion - 4.0+ satisfaction - No data loss

If Met: Proceed to Phase 2 If Not Met: Iterate for 2 more weeks


Next Phase Preview

Phase 2 (Weeks 9-16): - Fitness Challenge mini-app - Shopping Concierge mini-app - Mini-app marketplace (discovery) - WebSocket real-time sync (upgrade from polling) - Enhanced analytics


Appendix: Key Files to Create

app/miniapps/
β”œβ”€β”€ __init__.py
β”œβ”€β”€ event_store.py
β”œβ”€β”€ room_manager.py
β”œβ”€β”€ state_coordinator.py
β”œβ”€β”€ sync_service.py
β”œβ”€β”€ blocks/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ base.py
β”‚   β”œβ”€β”€ text.py
β”‚   β”œβ”€β”€ quick_reply.py
β”‚   β”œβ”€β”€ poll.py
β”‚   β”œβ”€β”€ card.py
β”‚   β”œβ”€β”€ table.py
β”‚   └── renderers/
β”‚       β”œβ”€β”€ imessage.py
β”‚       └── telegram.py
└── apps/
    └── trip_planner/
        β”œβ”€β”€ __init__.py
        β”œβ”€β”€ workflow.py
        β”œβ”€β”€ handlers.py
        └── state.py

app/superpowers/catalog/multiplayer/
└── trip_planner.py

tests/
β”œβ”€β”€ miniapps/
β”‚   β”œβ”€β”€ test_event_store.py
β”‚   β”œβ”€β”€ test_room_manager.py
β”‚   β”œβ”€β”€ test_state_coordinator.py
β”‚   └── test_blocks.py
└── integration/
    β”œβ”€β”€ test_trip_planner_e2e.py
    └── test_concurrent_voting.py

scripts/
β”œβ”€β”€ migrate_to_miniapps.py
└── setup_dynamodb.py

docs/
β”œβ”€β”€ miniapps/
β”‚   β”œβ”€β”€ architecture.md
β”‚   β”œβ”€β”€ trip-planner-guide.md
β”‚   └── developer-guide.md

Conclusion

Phase 1 establishes the foundational multiplayer architecture and validates it with Trip Planner. This sets the stage for rapid development of additional mini-apps in Phase 2 and beyond.

Key Principle: Start small, validate, then scale.