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 inapp/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¶
- Build Foundation: Event sourcing, room management, UI blocks
- Ship MVP: Trip Planner mini-app working in iMessage
- Validate Architecture: Prove multiplayer patterns work at small scale
- 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
-
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 -
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." ) -
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) -
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:
- Happy Path: Create Trip & Add Activities
- User A starts trip planner in group chat
- System creates room with all participants
- User A sets destination & dates
- User B proposes activity
- All users vote
-
Activity confirmed
-
Concurrent Voting
- User A and User B vote simultaneously
- Both votes recorded
- No data loss
-
Results updated correctly
-
Offline β Online Sync
- User A goes offline
- User B adds 3 activities
- User A comes back online
- Polls for updates
-
Receives all 3 activities
-
Error Handling
- Invalid vote (typo in number)
- Non-participant tries to join
- 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.