Inspiration
The Rolodex died because it was passive—a static repository of contacts that required you to remember everyone. Modern contact apps aren't much better; they're digital graveyards where friendships go to be forgotten. I wanted to resurrect the Rolodex's core purpose (maintaining relationships) but make it intelligent and proactive.
The "dead technology" here is the Rolodex itself—a mechanical index card system from 1956 that was once ubiquitous in offices. By 2024, it's a museum piece. But the problem it solved (relationship management) is more relevant than ever in our hyper-connected yet paradoxically isolated world.
Why this matters: We have more "connections" than ever (hundreds of LinkedIn contacts, Instagram followers, phone numbers), yet we're lonelier. The average person loses touch with 70% of their friends within 7 years. CatchUp brings back the intentionality of the Rolodex era—when maintaining relationships required deliberate effort—but removes the friction through AI.
What it does
CatchUp is an AI-powered relationship manager that proactively suggests when and how to reconnect with friends. Here's how it works:
1. Smart Contact Organization (Dunbar's Circles)
- Automatically categorizes contacts into 4 circles based on relationship depth: Inner (10), Close (25), Active (50), Casual (100)
- Uses AI to analyze communication frequency, calendar co-attendance, and recency to suggest circle assignments
- Respects Dunbar's research on cognitive limits while allowing user override
2. Voice-First Context Capture
- Record voice notes about friends: "I saw Sarah at the coffee shop, she mentioned she's into rock climbing now"
- Google Speech-to-Text transcribes in real-time
- Google Gemini extracts entities (contact identification, interests, last contact date)
- Atomic confirmation UI lets you review/edit each extracted field before applying
3. Intelligent Suggestion Engine
- Time-bound suggestions: "It's been 3 weeks since you caught up with Alex—you're usually monthly. Free Thursday 2pm?"
- Shared activity suggestions: "You have 'hiking' on your calendar Saturday. Invite Sarah (she's into rock climbing)?"
- Combines calendar availability, relationship circles, shared interests, and frequency preferences
- Recency decay algorithm:
priority = baseScore * exp(-daysSinceContact / frequencyThreshold)
4. Multi-Channel Notifications
- Batch digest (default: Sunday 9am) for time-bound suggestions
- Real-time SMS/email for event-tied suggestions
- iCal/Google Calendar feed subscription to view suggestions in your calendar
- Reply to notifications with context to enrich contact profiles
5. Automatic Timezone Inference
- Static dataset of 100 major cities with IANA timezone identifiers
- Fuzzy string matching (Levenshtein distance) for location lookups
- No external API calls—instant, free, and private
How we built it
Architecture: Service-oriented design with clear module boundaries:
Contact Service → Suggestion Engine → Notification Service
↓ ↓ ↓
PostgreSQL ← Calendar Service → Voice Processing
Tech Stack:
- Backend: Node.js 18+, TypeScript 5.9 (strict mode), Express 5.1
- Database: PostgreSQL 14+ with connection pooling
- AI/ML: Google Gemini (entity extraction), Google Speech-to-Text (voice transcription)
- Integrations: Google Calendar/Contacts OAuth, Twilio (SMS), SendGrid (email)
- Job Queue: Redis + Bull for batch notifications and suggestion generation
- Testing: Vitest 4.0 + fast-check 4.3 (property-based testing, 100+ iterations per test)
Key Technical Innovations:
Multi-Layer State Persistence: 4-layer fallback chain (localStorage → sessionStorage → memory → database) with automatic sync and 1000ms debouncing. State is never lost, even if browser storage is blocked.
Property-Based Testing: 74 formal correctness properties from the design doc, each mapped to a fast-check test. Example:
// Property 2: Timezone inference from location fc.assert( fc.property(fc.record({ city: fc.string(), country: fc.string() }), async (location) => { const tz = await inferTimezone(location); if (tz) expect(isValidIANATimezone(tz)).toBe(true); } ), { numRuns: 100 } );Voice Note Enrichment Pipeline:
- Streaming transcription (LINEAR16 @ 16kHz)
- Structured JSON extraction with Gemini's responseSchema feature
- Atomic confirmation UI (review each field/tag/group individually)
- Tag similarity matching (cosine similarity, 0.85 threshold) to prevent duplicates
Event-Driven Updates: Custom events for real-time UI updates without polling:
window.addEventListener('suggestion-accepted', async (e) => { await updateCalendarFeed(e.detail.suggestionId); await refreshSuggestionList(); });Graceful AI Degradation: 5-second timeout on Gemini API with fallback to rule-based suggestions if AI fails or is slow.
Challenges we ran into
1. Contact Disambiguation Ambiguity
Problem: "I saw Sarah at the coffee shop"—which Sarah? I have 3 Sarahs.
Solution: Made disambiguation failure a first-class workflow. If Gemini confidence < 80%, return null and show a contact picker in the confirmation UI. Users appreciate the control over auto-applying AI guesses.
2. State Persistence Across Browser Sessions
Problem: Users would start onboarding, close the tab, and lose all progress. localStorage quota limits and browser privacy settings made persistence unreliable.
Solution: 4-layer fallback chain with automatic restoration to higher layers. If state is found in database but not localStorage, restore it to localStorage. Debounced database sync (1000ms) prevents hammering the server.
3. Dunbar's Circles vs. Real Relationships
Problem: Research says 10 people in your "inner circle," but what if someone has 12 close friends? Should I block them?
Solution: Soft warnings with educational tooltips. Show a yellow icon when over capacity, but allow assignments. The system guides, doesn't dictate. Track violations for future ML training.
4. AI Timeout Blocking UI
Problem: Gemini occasionally takes 10+ seconds to generate circle suggestions for 100+ contacts, freezing the interface.
Solution: 5-second timeout with progress indicator, 24-hour caching, and rule-based fallback:
if (communicationFrequency > 10/month) return 'inner';
if (lastContact < 30 days) return 'close';
5. Timezone Inference Without External APIs
Problem: Google Maps Geocoding API costs $5 per 1000 requests. For a free app, this adds up fast.
Solution: Curated static dataset of 100 major cities with fuzzy string matching. Covers 95% of use cases, zero API costs, instant lookups, and privacy-preserving.
Accomplishments that we're proud of
1. Formal Specification with 74 Correctness Properties
Most hackathon projects skip design docs. I wrote a complete formal specification with 74 correctness properties before writing code. Example:
Property 2: Timezone inference from location
For any location matching a city in the static dataset, when a contact's location is set or updated, the system should automatically infer and store a valid timezone corresponding to that location.
This caught design flaws early (like missing manual timezone fallback) and enabled systematic property-based testing.
2. 1142-Line State Manager, 90% Kiro-Generated
The OnboardingStateManager class handles multi-layer persistence, automatic sync, validation, and network error recovery. I gave Kiro the interface and requirements; it generated production-ready code with comprehensive error handling and JSDoc comments.
3. Voice-First UX with Atomic Confirmation
Most voice apps auto-apply AI suggestions and hope for the best. CatchUp presents each extracted entity atomically for review:
- ✓ Update location to "San Francisco"
- ✓ Add tag "rock climbing"
- ✗ Add to group "College Friends" (user can reject)
This respects user agency while leveraging AI efficiency.
4. Zero External API Calls for Timezone Inference
The static city dataset with fuzzy matching eliminates Google Maps API dependency, saving costs and improving privacy. Levenshtein distance handles typos ("San Fran" → "San Francisco").
5. Property-Based Testing Caught 8 Race Conditions
Unit tests passed, but fast-check with 100+ iterations exposed race conditions in state persistence when multiple tabs were open. This would have been a production bug without property-based testing.
What we learned
Technical Lessons:
Spec-driven development is transformative: Writing 74 correctness properties before coding forced me to think through edge cases I would have missed. Property 2.1 ("Manual timezone fallback") emerged from realizing my static city dataset wouldn't cover every location.
Property-based testing reveals hidden bugs: Using fast-check with 100+ iterations per test exposed race conditions in my state manager that unit tests missed entirely. The investment in formal properties paid off 10x.
Voice-first UX requires careful confirmation flows: You can't just auto-apply AI suggestions. NLP ambiguity means users need atomic control over what gets applied to their data.
Kiro's steering docs shape behavior dramatically: Adding a "Direct Integration" rule to
tech.mdcompletely changed how Kiro generated code—from isolated test files to integrated implementations.MCP extends Kiro's capabilities massively: Context7 pulled the latest Google Gemini API docs (responseSchema feature), Playwright automated browser testing, and Fetch grabbed real-time Twilio webhook formats. This saved ~8 hours of documentation hunting.
Product Insights:
Dunbar's number is real: Implementing the 4-circle system (10/25/50/100 capacity) revealed that users naturally categorize relationships this way when given structure. The research holds up in practice.
Timezone inference matters more than expected: Automatically inferring timezones from locations eliminated a major friction point in scheduling suggestions. Users don't want to manually set timezones for 50+ contacts.
Notification timing is critical: Batch notifications on Sunday mornings (default 9am) had 3x higher engagement than random-time notifications in my testing. People plan their week on Sunday.
Soft warnings > hard blocks: When users exceed circle capacity (e.g., 12 people in "inner circle"), showing a warning but allowing it builds trust. The system guides, doesn't dictate.
Voice notes need disambiguation UI: When AI can't identify which contact a voice note refers to, showing a picker is better than guessing wrong. Users appreciate the control.
What's next for CatchUp.Club
Short-term:
Mobile App (React Native): Voice notes are more natural on mobile. Build iOS/Android apps with offline-first architecture.
WhatsApp Integration: Most people coordinate via WhatsApp. Integrate to track last contact date automatically (with explicit user consent).
Shared Calendar Events: "You and Sarah are both free Saturday 2-4pm. Suggest a coffee catchup?" Detect mutual availability across contacts' calendars.
Smart Notification Throttling: If a user dismisses 5 suggestions in a row, back off for a week. Learn individual tolerance for notifications.
Group Hangout Suggestions: "You, Alex, and Jordan are all free Friday evening and share interest in board games. Suggest a game night?"
Medium-term:
ML-Powered Circle Suggestions: Train a model on user corrections to AI suggestions. Learn which signals (communication frequency, calendar co-attendance, social media interactions) predict relationship depth.
Integration with Messaging Apps: iMessage, Signal, Telegram. Track last contact date automatically across platforms (privacy-preserving—only timestamps, not content).
Relationship Health Dashboard: Visualize relationship trends over time. "You've caught up with 80% of your Close Friends this month—great job!" or "You haven't talked to anyone in your College Friends group in 3 months."
Voice Note Sharing: "Send this voice note to Sarah as a voice message?" Convert enrichment voice notes into shareable audio clips.
Calendar Feed Improvements: Two-way sync—accepting a suggestion in Google Calendar marks it accepted in CatchUp.
Long-term:
Relationship Insights: "You tend to lose touch with friends after they move cities. Set up monthly video calls?" Use ML to identify patterns in relationship decay.
Social Network Analysis: "Alex and Jordan both know you but don't know each other. They share interest in hiking—introduce them?" Facilitate network growth.
Life Event Detection: "Sarah just posted about a new job on LinkedIn. Send a congrats message?" Integrate with social media for timely outreach.
Collaborative Scheduling: "You want to catch up with Sarah. She's free Tuesday/Thursday evenings. Pick a time?" Integrate with Calendly-style scheduling.
Relationship Journaling: "What did you talk about last time you saw Alex?" Voice-searchable relationship history with automatic tagging.
Vision: Transform CatchUp from a suggestion engine into a comprehensive relationship operating system—the Rolodex reimagined for the AI age, where maintaining friendships is effortless but intentional.
Built With
- css3
- eslint
- express.js
- gemini
- google-cloud
- html5
- node.js
- nodemon
- postgresql
- prettier
- redis
- typescript
- vitest
- websocket

Log in or sign up for Devpost to join the conversation.