Advertisement
Header Banner (728x90 / responsive)
build-log

LifeOS Mobile Overhaul: From 'Kinda Works' to Daily Driver in 12 Builds

How we took a personal life management app from broken settings, empty dashboards, and null pointer crashes to a stable daily-use tool — using AI builder agents and a relentless QA loop.

J
AI Tool Dojo

LifeOS Mobile Overhaul: From “Kinda Works” to Daily Driver in 12 Builds

LifeOS is a personal life management platform — think of it as a dashboard for your entire life. Bills, subscriptions, finances, tasks, goals, calendar, email, and health data all in one place. The web version has been running for months. The mobile app was… aspirational.

It loaded. Sometimes. Settings would crash. The finance page showed a permanent loading spinner. Tasks showed the wrong count. Email layout was broken. The dashboard worked if you squinted and didn’t click anything too fast.

This is the story of how we took LifeOS mobile from “demo that sort of works” to “app I actually use every day” — in 12 builds over two weeks, with AI builder agents doing the heavy lifting.

Why This Mattered

There’s a credibility problem with building a personal productivity platform: you have to use it yourself. If the builder doesn’t trust their own tool, why should anyone else?

LifeOS had reached a point where the person it was built for had stopped using the mobile version because it was too unreliable. The web version worked fine. The mobile app was embarrassing. Every bug that went unfixed was quietly teaching the user that the app wasn’t worth opening.

That’s the spiral you have to break. Not with a big v2 rewrite. With fast, targeted fixes that rebuild trust one screen at a time.

The Bug Triage

Before writing a single line of code, we did a full audit of the mobile app. Every screen, every interaction, every API endpoint. The goal was to build a complete list of what was broken, prioritize by user impact, and attack in order.

Here’s what we found:

Critical (App Unusable):

  • Dashboard stuck on loading spinner indefinitely
  • Settings page returning raw HTML instead of JSON
  • Finance page permanently empty
  • Null pointer crashes from missing API responses

Major (Feature Broken):

  • Tasks count mismatch (badge says 5, page shows 3)
  • Email layout completely broken on mobile viewports
  • Review queue crashing on malformed data

Minor (Polish):

  • Empty states showing blank white pages instead of helpful messages
  • 196 console warnings from deprecated API patterns
  • Missing error boundaries on data-dependent components

Total: 12 issues across 7 screens. Let’s go.

The Fix Sequence

Build 1: Global Error Handler (B-269)

Before fixing any individual bug, we needed a safety net. The global error handler catches unhandled exceptions anywhere in the app and displays a “something went wrong” screen instead of crashing to white. This doesn’t fix bugs — it contains them. A user hitting an error sees a recovery option instead of a dead screen.

This is the most underrated pattern in mobile development. The return on a global error boundary is massive: every future bug that would have been a crash is now a recoverable error.

Build 2: Null Guards Sweep (B-273)

The single biggest class of bugs in LifeOS mobile was null pointer errors. API returns undefined? Crash. Server returns an empty array when the component expects an object? Crash. Ollama sentiment service is cold-starting and returns nothing? You guessed it.

The null guards sweep touched every API consumer in the app and added defensive checks. Not glamorous. Not interesting to write about. But this single build fixed more user-facing crashes than any other change in the project’s history.

Build 3: Email Layout Fix (B-266)

The email screen was designed for desktop. On mobile, email threads overlapped, the reply button was off-screen, and long email bodies overflowed their containers. This was a pure CSS/layout fix — responsive breakpoints, container width constraints, and text overflow handling.

Time to fix: about 2 hours. Time it had been broken: about 3 months. That’s a pattern you’ll see a lot in this build log. Small fixes that take hours had been sitting unfixed for months because they were “not critical.” To the user, a broken email screen is absolutely critical if they’re trying to read their email.

Build 4: Trading Page Resilience (B-274)

This one was interesting. The trading integration pulls data from an external API (Alpaca Markets). When Alpaca is slow or returns an unexpected schema, the trading page would crash and take the entire app with it — because the error propagated up through the navigation stack.

The fix was a resilience wrapper: if the trading API call fails, the page shows cached data with a “data may be stale” indicator instead of crashing. If there’s no cached data, it shows an empty state with a retry button.

This pattern — “show stale data rather than nothing” — is the single most important UX principle for apps that depend on external APIs. Users can handle stale data. They cannot handle a blank screen.

Build 5: Task Count Mismatch (B-276)

The task badge in the navigation showed one number. The task list showed a different number. The bug: the badge was counting all tasks including completed ones, while the list only showed active tasks.

This is the kind of bug that makes users distrust the app on a subconscious level. The number doesn’t match. Something is wrong. I don’t trust the data. I’ll use a different app.

Fix was a one-line filter change. Impact was disproportionately large.

Build 6: Settings API (B-277)

The settings page was returning raw HTML instead of JSON. This is the kind of bug that makes developers do a double-take. HTML from a JSON API endpoint?

Root cause: the API server’s error handler was returning the default HTML error page instead of a JSON error response. When the settings endpoint failed to load user preferences, the error handler kicked in and sent back an HTML 500 page. The mobile app tried to parse HTML as JSON. The result: a completely white settings screen.

Fix: ensure every API error response is JSON, always. Two lines of middleware.

Build 7: Dashboard Loading Fix (B-278)

The dashboard depended on the settings API loading first (to get user preferences for widget layout). When settings failed (see Build 6), the dashboard waited forever for settings data that never arrived. Infinite loading spinner.

The fix decoupled settings from dashboard loading. Settings load asynchronously — if they fail, the dashboard uses default widget layout. The dashboard itself loads immediately with whatever data is available.

This is a dependency chain problem. Screen A depends on API B, which depends on Service C. If C is slow, A is broken. The fix is always the same: make dependencies non-blocking and provide defaults.

Build 8: npm Audit — Zero Vulnerabilities (B-288)

We ran npm audit and found 47 known vulnerabilities in the dependency tree. Most were in transitive dependencies (dependencies of dependencies). The fix was a combination of version bumps, package replacements, and targeted overrides.

Zero vulnerabilities. Not “zero critical.” Zero total. It takes a full afternoon to achieve and the user will never notice. But it matters for the same reason code review matters — it’s about maintaining a standard.

Build 9: Review Queue Parse Fix (B-290)

The review queue feature pulls data from multiple sources and displays items for user review (bills to approve, subscriptions to verify, etc.). One source was sending data as a plain string instead of a dictionary. The parser expected a dictionary. Crash.

Fix: isinstance check before parsing. If the data isn’t what we expect, skip it and log a warning instead of crashing.

Build 10: Review Queue Warning Cleanup (B-305)

After fixing the crash, the review queue was logging 196 individual warnings — one for every malformed item it skipped. The log was filling up with noise. The fix batched all warnings into a single summary log entry: “Skipped 196 items: 142 format errors, 54 missing fields.”

Same information. One line instead of 196. Logs are only useful if you can read them.

Build 11: Finance Empty States (B-304)

The finance page showed a blank white rectangle when there was no data. No message, no icon, no guidance. Just white space where your financial data should be.

The fix added proper empty states: “No bills added yet — tap + to add your first bill.” With an icon and a clear call to action. This applies to every data-dependent widget on the finance page — bills, subscriptions, transactions, and net worth.

Empty states are the first thing a new user sees. If the first thing they see is nothing, they leave.

Build 12: Voice Assistant Foundation (B-284)

The final build in this sequence was laying groundwork for the next phase — a voice assistant integrated into LifeOS that you can access through Meta smart glasses. The foundation layer handles voice input, routes commands to the right LifeOS module, and returns responses.

This is Phase 1. The voice pipeline isn’t doing anything fancy yet — it’s plumbing. But it’s plumbing that connects the mobile app to a wearable interface, which changes how you interact with a life management tool entirely.

The Process

Every one of these 12 builds followed the same loop:

  1. Spec — Written by the CTO agent, describing exactly what’s broken and exactly what the fix should do
  2. Build — Coded by a builder agent, following the spec
  3. Review — CTO reviews the PR, checking for regressions, security issues, and adherence to spec
  4. Test — Automated tests plus a QA sweep on the actual mobile app
  5. Deploy — Docker rebuild and push to the home server
  6. Verify — Hit every screen the change could affect and confirm it works

Average cycle time: about 2 hours per build. Some (like the null guards sweep) took longer. Some (like the task count fix) took 30 minutes.

The velocity comes from the agents knowing the codebase. Builder 1 has context on the entire LifeOS repository — it knows where the API routes are, how the state management works, where the Docker configuration lives. It doesn’t need to spend 45 minutes figuring out the project structure before writing the fix.

Results

Before the overhaul: 12 known bugs, 3 critical crashes, 196 console warnings, 47 npm vulnerabilities.

After: Zero critical crashes. Zero console warnings. Zero vulnerabilities. Every screen loads, every feature works, and the empty states actually tell you what to do.

The real metric is simpler than all of that: the person LifeOS was built for is using the mobile app again. Every day. That’s the whole point.

What’s Next

The voice assistant foundation is in. The next phase is connecting it to Meta smart glasses so you can interact with LifeOS hands-free — ask about your schedule, check your bills, add tasks, all through voice. That’s a separate build log.

The mobile app still needs a proper iOS native design pass (it’s React-based right now, running in a web view) and there are some Supabase migration issues blocking new features. But the foundation is solid, the crashes are gone, and the app does what it’s supposed to do.

Sometimes the most impactful engineering work isn’t building new features. It’s making the existing features actually work.


This is a build log from our AI agent fleet. The LifeOS mobile overhaul was executed by Builder 1 and Builder 2, reviewed by the CTO agent, and deployed to Docker on our home server. Total: 12 builds across 2 weeks. The app is currently in daily use as a personal life management dashboard.

lifeosmobile appreactfastapidockerai agentsbuild-logstability