Inspiration
My day was scattered across six apps. Tasks in one. Expenses in another. A notes app for whatever I was turning over. A habit tracker I fed for a week and then stopped. A goals app that wanted me to plan the year before it would let me plan Tuesday. And a breathing app I opened twice and then forgot about.
None of them knew the others existed. So the only place my day ever came together was in my head, which is exactly where I wanted it to stop living.
Two things pushed me from shopping to building.
The first is that I have always thought offline-first apps were a cool idea. Almost everything I tried was built the other way round, so the moment the signal dips you get a spinner instead of your day. I wanted to see if I could do the opposite, where the phone holds the real copy and the cloud is the one catching up.
The second is the khata (ledger). Families in India keep a running book of who owes what, and it is a social thing rather than an accounting one. Every app that touched it wanted to become a payments product, take a cut, and turn a note between two people into a transaction. I wanted the book, and none of that.
What it does
Clear Path holds nine parts of a day in one place, grouped into three areas.
Daily practice. Tasks with real recurrence, where completion is tracked per occurrence, so yesterday's tick never hides today's work. A task can carry a count goal, like 8 glasses or 30 pages or 5 km, and tick upward as the day goes on. It can carry an alarm too, for the ones that are not allowed to quietly slip. Repeat plus a goal is how a habit actually gets built here, without a separate habit app to keep fed. Then Focus sessions you can name and save as templates, and Zen, a guided breathing session for the gap between two things.
Reflection. A one-tap daily mood check-in that turns into a readable month. Thoughts, a local-first notebook. And a dashboard that puts momentum, today's mood, the remaining tasks and recent spending on one screen. Momentum is a ring that fills as you finish today's list, and you can flip it over to your daily streak instead. Zen and mood keep streaks of their own. Nothing shouts when one breaks. They are there to show you turned up, not to make you feel bad about a Tuesday.
Money. Expense capture in seconds, including scanning a UPI QR code to prefill the form, with bill splitting that does the arithmetic. Budgets as a boundary per category, with a ring that closes as you spend. And the ledger, a running balance per person that settles to zero.
Friends is not a fourth area, because it runs through the ones above rather than sitting beside them. You add someone by username, they accept, and after that a task can carry their name and avatar on the card instead of just yours, which is the Pro half of the feature. The part that is not built yet is delivery: the task is tagged, but it does not land on their phone. I would rather say that plainly than imply otherwise.
The whole thing is offline-first. The local database is the primary copy, not a cache, so every feature works with no signal and syncs when there is one.

How we built it
Flutter, one codebase, 300+ Dart files and roughly 100,000+ lines across 600+ commits since 11 February 2026.
Storage is local-first by design. Writes land in SQLite on the device and mirror to Cloud Firestore, rather than the other way around. That ordering is the one decision the entire offline story rests on.
Firebase Authentication handles identity, including Google sign-in, and the app never sees a password.
AI is Gemini 2.5 Flash Lite through Firebase AI, used for optional things only: drafting a task breakdown, improving note text, and parsing a dictated task. Every call is metered against a daily budget.
Subscriptions run on RevenueCat, wired server-authoritatively. A purchase fires a RevenueCat webhook. The official RevenueCat Firebase Extension writes the customer document into Firestore. The app streams that document to work out entitlement. There is no custom Cloud Function anywhere in that path, because the Extension's native document shape is already the contract. Firestore rules make that document read-your-own and never client-writable, so a modified client cannot grant itself Pro. Entitlement is cached in preferences so it survives being offline. I built the paywall myself rather than using the prebuilt one, so it matches the rest of the app.
The monetization line. Free gives you 100 tasks, 200 expenses, 6 accounts, one signed-in device and 4 AI calls a day. Pro raises those to 300 tasks, 600 expenses, 18 accounts, five devices and 12 AI calls a day, and adds cloud sync for notes and the ledger. I drew the line so the free tier stays genuinely usable for months instead of expiring into a trial. Pro sells continuity across devices and headroom. It does not sell access to the app.
Challenges we ran into
Pro users saw Free on the web build. The entitlement document had been sitting in Firestore the whole time. The cause was ordering. I awaited the RevenueCat login call before starting the Firestore listener, and on web the SDK is deliberately unconfigured, so that call hangs and the listener never starts. Reading the entitlement only ever needed Firestore, never a configured SDK, so the fix was to start the watch first and treat the login as best-effort.
A test key force-closes a release build. Moving from the Test Store to a real build, the app died instantly on launch with almost nothing useful in the log. RevenueCat rejects a test key in a release binary by design. Correct behaviour, and completely invisible until it happens to you. Keys now come from build-time defines rather than a bundled file, so debug and release cannot pick up each other's credentials.
Recurring tasks looked finished forever. Completing one made it vanish the next day. The model carries a legacy boolean that means "was completed at some point", while the real truth is a set of completed dates. Filtering on that boolean in SQL dropped tasks that were genuinely pending today, before any correction could run. I moved completion filtering out of SQL entirely, then found the same assumption had quietly broken alarm scheduling in four more places.
Voice capture silently lost every detail. Saying "read 30 pages every day at 9 PM" saved a task called "Read", with no time, no recurrence and no goal. The confirmation chips read correctly, which made it look like an AI accuracy problem. It was plumbing. The field showed the cleaned title, and the save path was parsing that field instead of the original sentence. Now the raw utterance is kept and parsed, and the AI result merges over the pattern-matched one rather than replacing it.
Accomplishments that we're proud of
An entitlement a client cannot forge. Pro status is written by a webhook and read under rules that forbid client writes. Patching the app buys you nothing, which is a property I would want even on a much larger team.
Offline-first that genuinely holds. Not a cache with a sync button. Every feature writes locally first and reconciles later, so the app behaves the same on a train as it does on wifi.
One dictated task costs one AI call. The suggestion chips and the save path share a single parse, keyed by the spoken phrase, instead of billing the budget twice for the same sentence. Editing the title afterwards does not re-parse. On a budget of four free calls a day, halving the cost of the most-used AI feature is the difference between a limit that feels generous and one that feels stingy.
115+ test files, concentrated on the seams that broke: recurrence, entitlement and the voice pipeline.
Six months, solo. Design, Flutter app, Firebase backend, subscription infrastructure, Android home-screen widgets and this website.
What we learned
A derived column will eventually lie to you. The recurring-task bug and the alarm bug were the same misplaced trust in one convenience flag. When I renamed it to say plainly that it means "was ever completed", the remaining misuses became obvious on sight. Naming a field after what it actually means is cheaper than any comment warning people about it.
Ordering bugs hide on the platform you test least. The RevenueCat login problem was invisible on Android and total on web. Anything awaited during sign-in is a place where one platform's no-op is another platform's deadlock.
Read the sponsor's failure modes before you need them. The test-key crash cost me an evening, and it was documented behaviour the whole time. Skimming the release checklist earlier would have turned it into a five-minute configuration step.
Meter AI where the work happens, not where the call happens. Budgeting per API call would have charged users twice for one sentence. Caching the parse by phrase let me set a smaller free limit that still feels fair.
What's next for Clear Path
Purchasing on the web, which needs RevenueCat Web Billing. Until it is live the web paywall says so honestly and points people to mobile rather than showing a button that cannot work.
iOS. The code is already platform-agnostic, so this is store setup and testing rather than a rewrite.
Shared tasks. Friends, requests and the assignment field are all in place, and a task already knows which friend it belongs to. What is missing is the last hop, getting it onto their device. I would rather ship that with the privacy wording and anti-spam rules written first than bolt them on afterwards.
Regional pricing that reads correctly everywhere, which the app already supports through store-driven prices and needs no code change once the products are configured.