This is a submission for the GitHub Copilot CLI Challenge
What I Built
About Me & My Project
My name is Moses Mtengo, a passionate software developer from Kenya who graduated with a BSc. in Telecommunications & IT from Machakos University in November 2025. My journey with Kotlin and modern UI development began during my time as a Google Developer Student Club Lead, where I organized Compose Camps to introduce fellow students to Jetpack Compose. This experience sparked a deep commitment to the Kotlin ecosystem, leading me to establish and lead the Kotlin User Group Machakos, where I continue mentoring developers in Kotlin Multiplatform technologies.
Beyond coding, I enjoy chess, gaming, and most importantly, building projects that create positive societal impactβsolutions born from real problems I've witnessed firsthand.
The Inspiration Behind Katiba
Katiba was inspired by Ng'ang'a Muigai, a Kenyan political activist who travels across the country teaching citizens about the 2010 Kenyan Constitutionβone of the most progressive constitutions in the world. He realized that citizens' lack of constitutional knowledge allows leaders to neglect their responsibilities. Moved by his mission, I reached out and promised to support his work by building a free mobile app. Katiba empowers Kenyans to learn the constitution at their own pace, eliminating the barriers of purchasing physical copies and making constitutional education accessible anywhere, anytime.
Technologies Used
Katiba is built with cutting-edge technologies including:
- Kotlin 2.1.0 with Compose Multiplatform 1.7.3 for shared Android/iOS UI
- Navigation 3 for type-safe, modern navigation
- Spring physics animations in the bottom navigation bar for fluid, natural interactions
- R8 optimizer for smaller, faster release builds
- Kotlin Coroutines and Serialization for efficient async operations
- Material Design 3 for a polished, accessible interface
This project represents my belief that technology should empower citizens and strengthen democracy through accessible education.
π― Key Features
1. Home Tab - Daily Learning Hub
The Home Tab serves as the primary engagement point, featuring three distinct cards that refresh daily:
Card 1: Clause of the Day
- Purpose: Introduce users to a single constitutional clause each day
- Content: Full text of the clause with chapter and article references
- Interaction: Single-page focused view for deep reading
- Actions: Share, bookmark, and save for later reference
Card 2: AI-Powered Insights & Video Education
- Purpose: Explain the significance and meaning of the daily clause
-
Content:
- Page 1: AI-generated explanation in plain language
- Page 2: Professional civic educator video (uploaded daily by admins)
- Navigation: Horizontal swipe or tap left/right to switch between pages
- Features: Video player with standard playback controls
Card 3: Practical Application Tips
- Purpose: Connect constitutional knowledge to daily life
-
Content:
- Page 1: AI-powered "next steps" - actions citizens can take
- Page 2: Practical tips for leveraging the clause in real-life situations
- Navigation: Multi-page horizontal navigation
- Value: Transforms abstract legal concepts into actionable insights
2. Constitution Tab - Digital Reader
Inspired by popular Bible reader apps, this tab provides a seamless reading experience:
Features
- Complete Text: Full Kenyan Constitution from Preamble through all Schedules
- Chapter Navigation: 18 chapters organized hierarchically
- Article & Clause Structure: Numbered sections for easy reference
- Vertical Scrolling: Natural reading flow for long-form content
- Quick Navigation: Jump to any chapter, article, or clause
- Search Function: (Planned) Find specific terms or concepts
- Bookmarks: (Planned) Mark important sections for reference
Content Structure
Preamble
βββ Chapter 1: Sovereignty of the People and Supremacy of the Constitution
βββ Chapter 2: The Republic
βββ Chapter 3: Citizenship
βββ Chapter 4: The Bill of Rights
β βββ General Provisions
β βββ Rights and Fundamental Freedoms
β βββ Application of Rights
βββ Chapter 5: Land
βββ Chapter 6: Leadership and Integrity
βββ Chapter 7: Representation of the People
βββ Chapter 8: The Legislature
βββ Chapter 9: The Executive
βββ Chapter 10: Judiciary
βββ Chapter 11: Devolved Government
βββ Chapter 12: Public Finance
βββ Chapter 13: The Public Service
βββ Chapter 14: National Security
βββ Chapter 15: Commissions and Independent Offices
βββ Chapter 16: Amendment of the Constitution
βββ Chapter 17: General Provisions
βββ Chapter 18: Transitional and Consequential Provisions
Schedules (7 total)
3. Learning Plans Tab - Gamified Education
Modeled after Duolingo's successful learning path approach:
Learning Path Structure
- Vertical Progress Journey: Visual representation of learning journey
- Milestone Nodes: Shield-shaped nodes representing chapters (culturally relevant)
- Lesson Modules: Bite-sized lessons covering constitutional topics sequentially
-
State Indicators:
- β Completed (green shield)
- π― Current (pulsing animation)
- π Locked (requires previous completion)
Gamification Elements
- XP Points: Earn experience points for completing lessons
- Streak Counter: Track consecutive days of learning
- Achievement Badges: Unlock badges for milestones
- Progress Bar: Visual feedback on overall completion
- Level System: Progress through beginner β intermediate β advanced
Lesson Structure
Each lesson includes:
- Introduction to topic
- Key concepts explanation
- Interactive quiz questions
- Real-world application examples
- Progress checkpoint
- XP reward upon completion
4. Profile Tab - Personal Civic Journey
Track your learning progress and civic engagement:
Profile Sections
Bio Card
- User name and avatar
- Join date
- Email/contact information
- Location details
Residence Information
- County selection
- Constituency
- Ward (optional)
- Purpose: Connect learning to local governance structures
Streak Display
- Current learning streak (consecutive days)
- Longest streak achieved
- Fire icon animation for active streaks
- Motivation for daily engagement
Badges Gallery
- Visual display of earned achievement badges
- Locked badges showing requirements
- Badge categories:
- Chapter completions
- Streak milestones
- Quiz performance
- Special achievements
Activity History
- Recent learning activities
- Lessons completed
- Clauses read
- Videos watched
- Time-stamped activity log
Settings Access
- Cog icon in top-right corner
- Access to app configuration
5. Settings Screen
Comprehensive app configuration:
Settings Categories
Account Settings
- Profile information
- Email/phone verification
- Password management
- Account deletion
Notification Preferences
- Daily clause reminders
- Streak notifications
- Achievement alerts
- Learning recommendations
- Quiet hours configuration
Display Preferences
- Light/Dark theme toggle
- Text size adjustment
- Reading mode preferences
- Accessibility options
Language Settings
- English/Swahili toggle
- Additional languages (future)
About Section
- App version
- Credits and acknowledgments
- Privacy policy
- Terms of service
- Contact support
Demo
GitHub Repository
Video walkthrough
My Experience with GitHub Copilot CLI
Building Katiba: Navigating Authentication with GitHub Copilot
Here's my situation: I work with Android development, Kotlin, and Jetpack Compose. I'm comfortable with mobile architecture, API integrations and UI development. But building a full authentication flow with OTP verification, password reset and proper error handling across multiple screens? That was a lot of ground to cover efficiently.
GitHub Copilot was my guide through the complex parts.
The First Conversation: "How Do I Structure This?"
I knew what I wanted to build β a constitutional guide app called Katiba with proper user authentication. I didn't know the best way to wire up the auth flow cleanly across the app.
I asked Copilot:
"How do I structure an authentication flow in Jetpack Compose with OTP verification and password reset?"
Copilot gave me a clear strategy:
- Use a
UserApiClientto centralize all API calls with proper token management. - Use Compose navigation with sealed routes to handle the multi-step auth flow.
- Don't scatter auth logic everywhere β use a profile-based approach where
getProfilevalidates the session andupdateProfilehandles user changes.
That last point was key. Instead of duplicating auth checks across every screen, I centralized it in UserApiClient so every request automatically includes the bearer token and handles errors consistently.
The Battle: Error Handling Across Screens
My auth flow worked perfectly on the happy path. Then I started testing edge cases.
Immediate confusion for the user.
The problem? Error messages from the API were getting swallowed or displayed inconsistently across ForgotPasswordScreen, the OTP screen and the profile update flow.
I asked Copilot:
"My error handling is inconsistent across auth screens. How do I standardize this?"
Copilot helped me refactor the error handling so every API response follows the same pattern β clear error states, proper callback naming and readable control flow. Simple fix. Now every screen handles errors the same way.
The Optimization: Simplifying Auth Checks
Early on, the authentication validation logic in UserApiClient was getting verbose and hard to follow. Multiple nested conditions, redundant checks.
I asked Copilot:
"How can I simplify these authentication checks without losing any security?"
Copilot helped me streamline the logic β cleaner conditionals, better method naming (like updating callback names in ForgotPasswordScreen for clarity) and removing unnecessary intermediate variables. The code went from walls of nested if statements to something I could actually read six months later.
The Readability Decision
I knew I needed to clean up getProfile and updateProfile. They worked, but the error handling was messy and the code was hard to follow for anyone else joining the project.
I asked Copilot:
"How can I improve the readability and error handling in these API methods?"
The answer: extract common patterns, use descriptive variable names, and let exceptions flow naturally instead of catching everything silently. Copilot walked me through the refactor step by step and the result was methods that are both more robust and easier to understand.
The Distribution Reality
Here's something I think about: how do users actually get Katiba on their devices?
The app needs to talk to a backend API, handle authentication tokens securely and manage user sessions β all while feeling native and fast on Android. Each piece has to work together seamlessly, from the Compose UI layer down to the network calls.
Copilot helped me think through the architecture so that each layer stays clean and independent. The API client handles networking, the ViewModels manage state and the Compose screens just render what they're told. Decoupled, testable and easy to extend when new features come along.
The Data Dilemma: JSON vs. YAML
Before I could even think about rendering the Constitution, I had to figure out how to store it. I had the raw text file (The_Constitution_of_Kenya_2010.txt), but dumping unstructured text into an app is a recipe for disaster. The app needed to know exactly what a "Chapter," "Article,", "Clause", "sub-Clause" and "mini-Clause" was so users could navigate it like a digital Bible.
I jumped into the terminal and asked GitHub Copilot CLI:
"I need to convert a massive text file of the Kenyan Constitution into a structured format to bundle in my Android app's assets. Should I use JSON or YAML for runtime parsing with Kotlin?"
Copilot CLI broke it down beautifully. While YAML is highly readable for humans, JSON is the undisputed champion for fast, native parsing on Android, especially when paired with kotlinx.serialization. JSON won.
The Python Parser
The next hurdle was actually doing the conversion. The Constitution has a deeply nested and somewhat inconsistent hierarchy: Chapters -> Parts (sometimes) -> Articles -> Clauses -> SubClauses -> MiniClauses. Plus, it was littered with page footers and sometimes paragraphs, tables and even images.
I used Copilot CLI to kickstart the parsing script:
"Write a Python script to parse a text file of the Constitution into a nested JSON structure. It needs to use regex to identify Chapters, Parts, Articles, Clauses (1), SubClauses (a) and MiniClauses (i)"
Copilot CLI generated a robust Python script that acted as my baseline. It used regular expressions to strip out the messy artifacts (``) and properly nest the arrays. When it struggled with edge casesβlike the tabular data in the Fifth ScheduleβI was able to iterate with Copilot to write a custom table parser just for that section.
Building the Data Repository
With kenya_constitution.json sitting in my assets folder, I needed a way to get it into the app's memory without blocking the main thread.
I asked Copilot CLI:
"How do I build a repository in Kotlin using Coroutines and kotlinx.serialization to read and parse a large JSON file from the assets folder?"
It gave me the exact boilerplate I needed: an AssetManager injected into a repository class, a suspend function running on Dispatchers.IO and the exact Json.decodeFromString syntax to map the data to my Kotlin data classes.
Debugging the Void
Everything compiled, but when I launched the app, my Reader Page was a ghost town. Some Chapters were missing their Articles and certain Clauses were completely swallowed.
The issue was the parsing logicβsome Articles didn't have Clauses and some Parts were entirely skipped by the Python script's regex.
I fed the error logs and snippets of the broken JSON back into Copilot CLI:
"My Kotlin data classes expect a list of SubClauses, but the JSON sometimes returns empty objects or skips from Article straight to text. How do I fix the parsing logic or handle these nullability issues in Kotlin?"
Copilot helped me tackle it from both ends. First, it helped me fix the Python regex to ensure every structural level was accounted for (even if it meant generating an empty array). Second, it suggested making my Kotlin data classes more resilient by assigning default empty lists (val clauses: List<Clause> = emptyList()). Suddenly, the data populated.
Refining the Reader UI
The final piece of the puzzle was the Reader Page itself. Rendering a massive, deeply nested document in Jetpack Compose without dropping frames requires a solid LazyColumn. I also needed the hierarchy to be visually distinctβChapters needed to look heavy and authoritative, while SubClauses needed proper indentation.
I asked Copilot:
"How do I build a Jetpack Compose LazyColumn to render a deeply nested Constitution structure? I need distinct typography for Chapters, Articles and indented text for Clauses."
Copilot scaffolded a clean UI utilizing Compose's item and items DSL. It guided me in creating modular composables (ChapterHeader, ArticleTitle, ClauseText) and applying Modifier.padding dynamically based on the depth of the nesting.
The result? A butter-smooth scrolling experience where users can seamlessly read the supreme law of the land, perfectly formatted and deeply navigable.
Developer
Testing For Judges
The app currently runs on Android but iOS support will be available soon. You can access the apk, for testing on an android device, via the Releases tab on the GitHub Repo linked above.
You can create an account via Google Signup to begin accessing the app.
Top comments (0)