01 · Agency Operations · Time Tracking SaaS
Agency Hub: how I took a vibe-coded Lovable build to live users with Claude Code, through Lovable's own BOLA disclosure.
In April 2026 I took over a Lovable-built operations dashboard on Upwork. The founder's brief was two sentences: we're onboarding live users, get the product ready. In the prep I found 73 bugs in Lovable's original build, fixed every one, and shipped the product. Six weeks later the app was stable, the Chrome extension was in the Web Store, and the project had already survived its first platform-wide BOLA security disclosure.
A founder hired me through Upwork in April 2026 to ship a Lovable build to live users. Good fit. I had just started using vibe-coding tools on my own projects. The brief was two sentences: we’re onboarding live users, get the product ready.
What I got on day one was a working preview I could log into and a folder of code. Login worked. The timer did not. The forms accepted negative numbers. The auth was permissive. The database had no audit trail. The workspace was also undocumented, with no GitHub connection and no list of bugs, but undocumented handovers are common on any project. The product symptoms are what made this one specifically a Lovable rescue.
The product is called Agency Hub. It is the operations dashboard an agency owner runs the business on. Clients, projects, freelancers, time entries, budgets. A Chrome extension that lets freelancers track time without leaving their browser. Two roles, one app. Admins see everything. Freelancers see what is theirs.
Six weeks later the app was live. The Chrome extension was in the Chrome Web Store. Seventy-three bugs from the original Lovable build had been found and closed during the prep. The product was stable, secure, and audit-trailed. It had already survived its first platform-wide BOLA security disclosure.
The admin Dashboard. Counters, revenue rollups, per-client margins. The data scoping is what matters, not the layout.
Week 1 · CLAUDE.md + ISSUES.md · Workspace first
Before any code, the workspace.
Most of the rescue work on a vibe-coded handover does not happen inside the code. It happens in the workspace around it. Handovers usually arrive light on docs, AI-assisted or otherwise. What is specific to AI-assisted builds is the missing commit reasoning. On a hand-built project you can read commit messages and reconstruct why something was built a particular way. On an AI-assisted build, the commits read “Changes” or “Work in progress” and the chat history where the actual decisions happened lives outside the repo.
My first commit to the repository was not a code fix. It was a document at the project root that lists every system the AI is allowed to touch, every rule it has to follow, and every credential boundary in the project. A few hours later I opened a second file and logged the first eighteen bugs I had found while exploring the product. By the end of the first week the project had a working GitHub connection and a prompts folder where every AI session is archived. The Linear board for the founder came a few weeks later.
I did not know it yet, but the workspace would matter more than any single fix.
The Projects ledger. Per-project billing types, active and paused statuses, hours and budget rollups. Profit and margin redacted.
Workflow · Claude Code + Lovable · GitHub bridge
Two AIs in a hierarchy, one human between them.
I run the two AIs in a hierarchy, with a human gate between them.
Claude Code, which I run from my laptop, plays the architect and the QA. It reads the codebase, drafts the plan for whatever is next, talks it through with me, and verifies every commit Lovable lands.
Lovable, which runs in the founder’s preview environment, plays the implementer. It has the database access. It writes the code. It also reviews Claude Code’s plans before any of them ship, as a second set of eyes.
GitHub is the bridge. Plans become files committed to a prompts folder. Lovable picks them up, executes them, commits the result. Claude Code pulls the result back and verifies it against the plan it wrote. I am the gate at every step. I approve the plan. I read the implementation. I run a manual smoke test. I back that up with a Playwright pass over every surface the change touched.
The founder is not in this loop on the technical side. He writes tickets in Linear and updates in Slack. He reads the demos. None of the rest is his job.
Weeks 2–6 · Supabase auth, RLS, audit log · Hardening
Closing the layers Lovable left open.
Lovable shipped a working dashboard. It also shipped a working dashboard that anyone with a browser could sign up to. Forms accepted negative hours and unbounded budgets. The most consequential table in the database had no audit trail. The timer recorded a freelancer’s late-night entry on the wrong calendar day.
None of those are exotic bugs. They are the layers nobody asks an AI tool to think about. The kind that ship working in the demo and break under the first real user.
The signup endpoint went from open to invite-only. Admins invite freelancers, the freelancer gets a magic-link email, the system links them to their role on first login. One atomic deploy. No window where a stranger could slip through. The longer version of this pattern is at Two-layer identity models in Supabase: when auth and authorization disagree.
Every form on the dashboard moved from the ad-hoc validation Lovable ships by default to a single declarative schema. The same checks run whether the form is submitted from the web app, the edit dialog, or the Chrome extension. Six pages migrated across four working days, one page per phase, with the build and a manual smoke pass as the gate between phases. Zero regressions in production. The methodology is at Phased migrations with per-phase verification gates.
The time-entry table got an audit log. Every edit and delete writes to a parallel record only admins can read. A freelancer who deletes the wrong row at midnight on a Sunday does not lose the data; the “Recently Deleted” panel in the admin surface reconstructs deletions from the trail.
The timezone-sensitive date math was the kind of bug that ships invisibly. A freelancer logs ninety minutes at 11:45pm in their own timezone. Without the fix, that entry lands on the wrong calendar day in the admin’s weekly report, and nobody notices until billing goes out. With the fix, every date passes through one anchored helper.
The Freelancers surface. Status is either Active (invited and signed in) or Not Invited (waiting on the invite). Public signup is closed at the project level.
The freelancer’s own dashboard. Hours, earnings, rate, history. Simpler than the admin surface by design.
The Submit Time Entry modal. The same validation schema runs here, in the admin edit dialog, and in the Chrome extension.
The admin Time Tracking view. Every edit and delete writes to a parallel audit table only admins can read.
May 12, 2026 · Chrome MV3 · Web Store launch
Live in the Chrome Web Store.
The Chrome extension is the freelancer’s daily surface. The web app is for reconciliation. The extension is the timer. Sign-in inside the popup. Persistent timer state. Project and task dropdowns filtered to whatever the freelancer is assigned to.
Getting it into the Chrome Web Store was the long pole. Trader verification under the EU Digital Services Act takes one to two weeks for a new business entity. The publisher identity choice (individual vs organisation) is structural and hard to change after submission. The listing URL has two valid forms; one of them silently fails to load in some browsers. None of these are in the developer documentation. All of them are blocking. The version of this story I wrote down is at Shipping a Manifest V3 Chrome extension: the gates nobody mentions.
Inside the repo I built a small enforcement layer: a pre-commit hook plus a GitHub Actions check that refuses any change inside the extension folder unless the manifest version is also bumped in the same commit. Without it, it is too easy to ship a fix locally and forget to bump the version, then watch the Web Store reject the upload three days later. The pattern is at Chrome extension version-bump discipline: pre-commit + GitHub Actions.
The extension went live on May 12, 2026.
The install page inside the dashboard. One-click route to the Chrome Web Store listing for new freelancers.
Sign-in inside the Manifest V3 popup. A small storage adapter keeps the session alive between popup opens and browser restarts.
The timer popup on first open. Project and task lists filtered to whatever the freelancer is assigned to.
Live timer state. Running time persists between popup opens; the today’s-total in the footer aggregates the freelancer’s own entries for the day.
April 20, 2026 · Lovable BOLA · 6-hour rotation
The day Lovable’s own platform got BOLA disclosed.
On April 20, 2026, a security researcher named Matt Palmer publicly disclosed a critical authorisation vulnerability in Lovable’s platform. Five API calls from a free Lovable account were enough to enumerate other users and pull the source code of their public projects. On a typical Lovable build that means database credentials, third-party API keys, and the AI chat history that exposes the rest of the architecture. Every project created before November 2025 was potentially in scope.
The platform I was running for the client was past that cutoff. The platform’s secrets were auto-injected at runtime rather than written into source. The auth logs showed nothing anomalous in the previous two weeks. Three checks, under an hour, defensible “likely not vulnerable.”
I rotated everything anyway. Six hours, in order: the Supabase API keys, then the email-service token, then the cron-scheduled-task shared secret, then the Chrome extension, then a manual end-to-end pass through every authenticated user flow before closing the rotation. The full play-by-play is at How to audit a Lovable app after the BOLA disclosure: a 6-hour rotation playbook.
The rotation took six hours and not three days because the workspace was already there. Every credential boundary was named in the project’s root document. The auth pattern from the first week was already written down. The prompts folder was grep-able for “secret” and “token.” Without that groundwork, the same rotation is a multi-day archaeological dig.
Rotation hour 5 · Supabase SDK · 158 → 67 lines
The migration that fell out of the rotation.
The hardest part of the rotation was the Chrome extension. It was on hand-rolled HTTP calls that worked under Supabase’s legacy API keys and returned 401 on every authenticated request once the new key format went live. The platform’s key format had shifted underneath the extension; the rotation was the moment that surfaced.
I deleted the hand-rolled wrappers and migrated the extension to the official Supabase JavaScript SDK. One hundred and fifty-eight lines of hand-rolled code became sixty-seven lines of SDK calls, with refresh, retry, and error handling the originals did not have. Then one more seam: the SDK defaults to a session-storage path that does not exist inside a Manifest V3 popup. A small adapter pointed it at the right one, and sessions persisted between popup opens again.
The full walkthrough: Migrating to Supabase publishable keys broke my Chrome extension. Here is the fix.
Manual entry mode for hours captured away from the timer. Same fields, no running clock, same validation as the web form.
Week after BOLA · Claude Code hooks · Safety net
A gate so the AI cannot drop a table.
When an AI can propose database changes against a live production project, the cost of one bad prompt is your customer’s data. In the week after the BOLA rotation I installed the safety net the project had been missing.
Claude Code has an explicit deny list for the commands that touch production data directly. A hook in front of every proposed migration pattern-matches for destructive shapes (drop tables, truncate, delete without where) and rejects them before the tool ever runs. The system prompt the AI reads at the start of every session repeats one rule: live data is sacrosanct.
The three layers are redundant on purpose. One stops the easy mistake. The next stops the proposed-by-accident migration. The last is a written reminder the AI reads first.
The today’s-summary expanded. Aggregates the freelancer’s own entries only.
Outcomes · Milestone 1 shipped · What I’d do again
The pattern I’d do again.
What I would do again on the next AI-assisted handover. Write the docs and the issue tracker before any feature work. Disable signup before anything else. Add the safety hooks before the first AI session, not after the first incident. Run two AI tools only if there is a human gate between them. The rotation took hours instead of days because the documentation was already there.